Compare commits
18 Commits
ed105c3857
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a701d0291 | ||
|
|
913c40b9ec | ||
|
|
e06ec3d5df | ||
|
|
42174ac9f7 | ||
|
|
30fbe7be35 | ||
|
|
44aa84a127 | ||
|
|
c3375f6cfd | ||
|
|
70228b655f | ||
|
|
3899e37f1e | ||
|
|
7ef669f852 | ||
|
|
ad7c6f2b65 | ||
|
|
c1872d10c8 | ||
|
|
8411e46811 | ||
|
|
9fa0d047eb | ||
|
|
1e47e4d892 | ||
|
|
884ca29046 | ||
|
|
1450d36a63 | ||
|
|
8ed8764b5d |
@@ -0,0 +1,11 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
Rule Name: development_workflow.mdc
|
||||
Description:
|
||||
- Do not move on to the next step of a task until user has confirmed the previous stage is finished. Pause occasionally and ask the user to accept changes.
|
||||
- Run tests after changing code functionality.
|
||||
- Use TDD principles where applicable.
|
||||
- Before implementing code, consider if it is worth refactoring any code in the working area to be more clean, modular, and readable.
|
||||
@@ -23,6 +23,21 @@
|
||||
{
|
||||
"ignorePseudoClasses": ["global"]
|
||||
}
|
||||
]
|
||||
],
|
||||
"color-named": "never",
|
||||
"color-no-hex": true,
|
||||
"function-disallowed-list": [
|
||||
"rgb",
|
||||
"rgba",
|
||||
"hsl",
|
||||
"hsla"
|
||||
],
|
||||
"declaration-property-value-disallowed-list": {
|
||||
"color": ["/^#/", "/^rgb/", "/^hsl/"],
|
||||
"background-color": ["/^#/", "/^rgb/", "/^hsl/"],
|
||||
"border-color": ["/^#/", "/^rgb/", "/^hsl/"],
|
||||
"fill": ["/^#/", "/^rgb/", "/^hsl/"],
|
||||
"stroke": ["/^#/", "/^rgb/", "/^hsl/"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Mantine Vite template
|
||||
|
||||
The project is a well-structured, modern React application with a clean design and strong technical foundation. It effectively showcases Craig Macfadyen's professional expertise but would benefit from completing the missing features and addressing the navigation structure. The use of Mantine provides a solid UI foundation, and the component architecture supports maintainability and future expansion.
|
||||
|
||||
## Features
|
||||
|
||||
This template comes with the following features:
|
||||
|
||||
182
frontend/vite-template-master/color-tokens.md
Normal file
182
frontend/vite-template-master/color-tokens.md
Normal file
@@ -0,0 +1,182 @@
|
||||
# Color Tokens Documentation
|
||||
|
||||
This document provides guidelines for using our standardized color system across the application.
|
||||
|
||||
## Color Palette Structure
|
||||
|
||||
Each color in our system has 10 shades, numbered from 0 to 9, where:
|
||||
- 0 is the lightest shade
|
||||
- 9 is the darkest shade
|
||||
- 5-6 are the base/primary shades
|
||||
|
||||
## Brand Colors
|
||||
|
||||
### Brand Primary (Blue)
|
||||
|
||||
This is our main brand color, used for primary actions, links, and brand identity elements.
|
||||
|
||||
| Token | Hex | Usage |
|
||||
|-------|-----|-------|
|
||||
| `var(--mantine-color-brand-primary-0)` | `#EAF4FF` | Background on hover states |
|
||||
| `var(--mantine-color-brand-primary-1)` | `#D5E9FF` | Background of informational elements |
|
||||
| `var(--mantine-color-brand-primary-2)` | `#B0D3FF` | Secondary backgrounds |
|
||||
| `var(--mantine-color-brand-primary-3)` | `#8BBDFF` | Borders, separators |
|
||||
| `var(--mantine-color-brand-primary-4)` | `#66A7FF` | Hover states for interactive elements |
|
||||
| `var(--mantine-color-brand-primary-5)` | `#4191FF` | Icons, secondary buttons |
|
||||
| `var(--mantine-color-brand-primary-6)` | `#1C7BFF` | **Primary buttons, links, focus states** |
|
||||
| `var(--mantine-color-brand-primary-7)` | `#0065F2` | Active/pressed states |
|
||||
| `var(--mantine-color-brand-primary-8)` | `#0055CC` | Tertiary text |
|
||||
| `var(--mantine-color-brand-primary-9)` | `#004099` | Text on light backgrounds |
|
||||
|
||||
### Brand Secondary
|
||||
|
||||
This complementary color is used for secondary UI elements and visual hierarchy.
|
||||
|
||||
| Token | Hex | Usage |
|
||||
|-------|-----|-------|
|
||||
| `var(--mantine-color-brand-secondary-0)` | `#F0F4F9` | Alternative background |
|
||||
| `var(--mantine-color-brand-secondary-1)` | `#E1E9F4` | Secondary hover backgrounds |
|
||||
| `var(--mantine-color-brand-secondary-2)` | `#C3D4E9` | Disabled backgrounds |
|
||||
| `var(--mantine-color-brand-secondary-3)` | `#A5BFDE` | Borders, dividers |
|
||||
| `var(--mantine-color-brand-secondary-4)` | `#87AAD3` | Non-primary icons |
|
||||
| `var(--mantine-color-brand-secondary-5)` | `#6995C8` | Highlighted text |
|
||||
| `var(--mantine-color-brand-secondary-6)` | `#4B80BD` | **Secondary buttons, links** |
|
||||
| `var(--mantine-color-brand-secondary-7)` | `#2D6BB2` | Active/pressed states for secondary elements |
|
||||
| `var(--mantine-color-brand-secondary-8)` | `#1F5A99` | Secondary text on light backgrounds |
|
||||
| `var(--mantine-color-brand-secondary-9)` | `#114480` | Dark text on light backgrounds |
|
||||
|
||||
### Brand Accent
|
||||
|
||||
This color is used for accent elements, call-to-actions, and highlighting important UI elements.
|
||||
|
||||
| Token | Hex | Usage |
|
||||
|-------|-----|-------|
|
||||
| `var(--mantine-color-brand-accent-0)` | `#FFF0EA` | Highlight backgrounds |
|
||||
| `var(--mantine-color-brand-accent-1)` | `#FFE1D5` | Notification backgrounds |
|
||||
| `var(--mantine-color-brand-accent-2)` | `#FFC3AD` | Secondary accent backgrounds |
|
||||
| `var(--mantine-color-brand-accent-3)` | `#FFA584` | Accent borders |
|
||||
| `var(--mantine-color-brand-accent-4)` | `#FF875B` | Accent hover states |
|
||||
| `var(--mantine-color-brand-accent-5)` | `#FF6932` | Secondary CTAs |
|
||||
| `var(--mantine-color-brand-accent-6)` | `#FF4A09` | **Primary CTAs, important actions** |
|
||||
| `var(--mantine-color-brand-accent-7)` | `#D93A00` | Active/pressed states for accent elements |
|
||||
| `var(--mantine-color-brand-accent-8)` | `#B02F00` | Dark accent text on light backgrounds |
|
||||
| `var(--mantine-color-brand-accent-9)` | `#882400` | Very dark accent, use sparingly |
|
||||
|
||||
### Brand Gray
|
||||
|
||||
This is our neutral color, used for text, backgrounds, borders, and non-emphasized UI elements.
|
||||
|
||||
| Token | Hex | Usage |
|
||||
|-------|-----|-------|
|
||||
| `var(--mantine-color-brand-gray-0)` | `#F8F9FA` | Page backgrounds, light mode |
|
||||
| `var(--mantine-color-brand-gray-1)` | `#F1F3F5` | Card/element backgrounds |
|
||||
| `var(--mantine-color-brand-gray-2)` | `#E9ECEF` | Alternative backgrounds, hover states |
|
||||
| `var(--mantine-color-brand-gray-3)` | `#DEE2E6` | **Borders, dividers, separators** |
|
||||
| `var(--mantine-color-brand-gray-4)` | `#CED4DA` | Disabled elements, secondary borders |
|
||||
| `var(--mantine-color-brand-gray-5)` | `#ADB5BD` | **Placeholder text, disabled text** |
|
||||
| `var(--mantine-color-brand-gray-6)` | `#868E96` | **Secondary text** |
|
||||
| `var(--mantine-color-brand-gray-7)` | `#495057` | **Primary text** |
|
||||
| `var(--mantine-color-brand-gray-8)` | `#343A40` | Headings, emphasized text |
|
||||
| `var(--mantine-color-brand-gray-9)` | `#212529` | Extra dark text, header text |
|
||||
|
||||
## Semantic Colors
|
||||
|
||||
### Success
|
||||
|
||||
Used for positive actions, success messages, and confirmations.
|
||||
|
||||
| Token | Hex | Usage |
|
||||
|-------|-----|-------|
|
||||
| `var(--mantine-color-success-0)` | `#EAFAF1` | Success background |
|
||||
| `var(--mantine-color-success-1)` | `#D5F5E3` | Light success background |
|
||||
| `var(--mantine-color-success-2)` | `#ABEBC6` | Secondary success background |
|
||||
| `var(--mantine-color-success-3)` | `#82E0AA` | Success borders |
|
||||
| `var(--mantine-color-success-4)` | `#58D68D` | Success hover states |
|
||||
| `var(--mantine-color-success-5)` | `#2ECC71` | Secondary success elements |
|
||||
| `var(--mantine-color-success-6)` | `#27AE60` | **Primary success elements, icons** |
|
||||
| `var(--mantine-color-success-7)` | `#229954` | Active/pressed states |
|
||||
| `var(--mantine-color-success-8)` | `#1E8449` | Dark success text |
|
||||
| `var(--mantine-color-success-9)` | `#196F3D` | Very dark success, use sparingly |
|
||||
|
||||
### Warning
|
||||
|
||||
Used for warnings, alerts that need attention but aren't critical.
|
||||
|
||||
| Token | Hex | Usage |
|
||||
|-------|-----|-------|
|
||||
| `var(--mantine-color-warning-0)` | `#FEF9E7` | Warning background |
|
||||
| `var(--mantine-color-warning-1)` | `#FCF3CF` | Light warning background |
|
||||
| `var(--mantine-color-warning-2)` | `#F9E79F` | Secondary warning background |
|
||||
| `var(--mantine-color-warning-3)` | `#F7DC6F` | Warning borders |
|
||||
| `var(--mantine-color-warning-4)` | `#F4D03F` | Warning hover states |
|
||||
| `var(--mantine-color-warning-5)` | `#F1C40F` | Secondary warning elements |
|
||||
| `var(--mantine-color-warning-6)` | `#D4AC0D` | **Primary warning elements, icons** |
|
||||
| `var(--mantine-color-warning-7)` | `#B7950B` | Active/pressed states |
|
||||
| `var(--mantine-color-warning-8)` | `#9A7D0A` | Dark warning text |
|
||||
| `var(--mantine-color-warning-9)` | `#7D6608` | Very dark warning, use sparingly |
|
||||
|
||||
### Error
|
||||
|
||||
Used for errors, destructive actions, and critical alerts.
|
||||
|
||||
| Token | Hex | Usage |
|
||||
|-------|-----|-------|
|
||||
| `var(--mantine-color-error-0)` | `#FDEDEC` | Error background |
|
||||
| `var(--mantine-color-error-1)` | `#FADBD8` | Light error background |
|
||||
| `var(--mantine-color-error-2)` | `#F5B7B1` | Secondary error background |
|
||||
| `var(--mantine-color-error-3)` | `#F1948A` | Error borders |
|
||||
| `var(--mantine-color-error-4)` | `#EC7063` | Error hover states |
|
||||
| `var(--mantine-color-error-5)` | `#E74C3C` | Secondary error elements |
|
||||
| `var(--mantine-color-error-6)` | `#CB4335` | **Primary error elements, icons** |
|
||||
| `var(--mantine-color-error-7)` | `#B03A2E` | Active/pressed states |
|
||||
| `var(--mantine-color-error-8)` | `#943126` | Dark error text |
|
||||
| `var(--mantine-color-error-9)` | `#78281F` | Very dark error, use sparingly |
|
||||
|
||||
## Semantic Tokens
|
||||
|
||||
For common UI elements, use these semantic tokens:
|
||||
|
||||
| Token | Maps to | Usage |
|
||||
|-------|--------|-------|
|
||||
| `var(--mantine-other-headerBgColor)` | `var(--mantine-color-body)` | Header background |
|
||||
| `var(--mantine-other-headerBorderColor)` | `var(--mantine-color-brand-gray-3)` | Header borders |
|
||||
| `var(--mantine-other-navigationActiveColor)` | `var(--mantine-color-brand-primary-6)` | Active navigation items |
|
||||
| `var(--mantine-other-navigationHoverColor)` | `var(--mantine-color-brand-primary-0)` | Hover state for navigation |
|
||||
| `var(--mantine-other-buttonPrimaryBg)` | `var(--mantine-color-brand-primary-6)` | Primary button background |
|
||||
| `var(--mantine-other-buttonSecondaryBg)` | `var(--mantine-color-brand-secondary-6)` | Secondary button background |
|
||||
| `var(--mantine-other-buttonAccentBg)` | `var(--mantine-color-brand-accent-6)` | Accent button background |
|
||||
|
||||
## Usage in CSS
|
||||
|
||||
### For direct CSS usage
|
||||
|
||||
```css
|
||||
.my-element {
|
||||
color: var(--mantine-color-brand-primary-6);
|
||||
background-color: var(--mantine-color-brand-gray-0);
|
||||
border: 1px solid var(--mantine-color-brand-gray-3);
|
||||
}
|
||||
```
|
||||
|
||||
### For light/dark mode support
|
||||
|
||||
```css
|
||||
.my-element {
|
||||
color: light-dark(var(--mantine-color-brand-gray-7), var(--mantine-color-brand-gray-0));
|
||||
background-color: light-dark(var(--mantine-color-brand-gray-0), var(--mantine-color-brand-gray-8));
|
||||
}
|
||||
```
|
||||
|
||||
## Usage in Components (with Mantine)
|
||||
|
||||
```tsx
|
||||
import { Button } from '@mantine/core';
|
||||
|
||||
// Using theme colors
|
||||
<Button color="brand-primary">Primary Button</Button>
|
||||
<Button color="brand-secondary">Secondary Button</Button>
|
||||
<Button color="brand-accent">Accent Button</Button>
|
||||
|
||||
// Using specific shade
|
||||
<Button color="brand-primary.6">Custom Shade Button</Button>
|
||||
```
|
||||
@@ -2,12 +2,12 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
|
||||
<link rel="icon" type="image/png" href="/src/assets/darkmode-small.png" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="minimum-scale=1, initial-scale=1, width=device-width, user-scalable=no"
|
||||
/>
|
||||
<title>Vite + Mantine App</title>
|
||||
<title>Craig Macfadyen</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
2460
frontend/vite-template-master/llms.txt
Normal file
2460
frontend/vite-template-master/llms.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
frontend/vite-template-master/src/assets/darkmode-small.png
Normal file
BIN
frontend/vite-template-master/src/assets/darkmode-small.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,16 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { ColorPalette } from './ColorPalette';
|
||||
|
||||
const meta = {
|
||||
title: 'Design System/ColorPalette',
|
||||
component: ColorPalette,
|
||||
tags: ['autodocs'],
|
||||
parameters: {
|
||||
layout: 'padded',
|
||||
},
|
||||
} satisfies Meta<typeof ColorPalette>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {};
|
||||
@@ -0,0 +1,154 @@
|
||||
import { Box, Group, Stack, Text, Title, useMantineTheme } from '@mantine/core';
|
||||
|
||||
interface ColorSwatchProps {
|
||||
colorName: string;
|
||||
shade: number;
|
||||
hex: string;
|
||||
}
|
||||
|
||||
function ColorSwatch({ colorName, shade, hex }: ColorSwatchProps) {
|
||||
return (
|
||||
<Box>
|
||||
<Box
|
||||
style={{
|
||||
backgroundColor: hex,
|
||||
width: '100%',
|
||||
height: 50,
|
||||
borderRadius: 5,
|
||||
marginBottom: 5,
|
||||
border: '1px solid var(--mantine-color-brand-gray-3)',
|
||||
}}
|
||||
/>
|
||||
<Text size="xs" fw={700}>
|
||||
{colorName}-{shade}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed">
|
||||
{hex}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
interface ColorGroupProps {
|
||||
title: string;
|
||||
colorName: string;
|
||||
description: string;
|
||||
colors: readonly string[];
|
||||
}
|
||||
|
||||
function ColorGroup({ title, colorName, description, colors }: ColorGroupProps) {
|
||||
return (
|
||||
<Stack gap="md">
|
||||
<Box mb={10}>
|
||||
<Title order={3}>{title}</Title>
|
||||
<Text size="sm">{description}</Text>
|
||||
</Box>
|
||||
<Group grow>
|
||||
{colors.map((color, index) => (
|
||||
<ColorSwatch key={index} colorName={colorName} shade={index} hex={color} />
|
||||
))}
|
||||
</Group>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export function ColorPalette() {
|
||||
const theme = useMantineTheme();
|
||||
|
||||
return (
|
||||
<Stack gap="xl">
|
||||
<Title order={1}>Color Palette Documentation</Title>
|
||||
<Text>
|
||||
This component showcases all colors defined in our theme system. Use these colors
|
||||
consistently throughout the application for a cohesive design.
|
||||
</Text>
|
||||
|
||||
<ColorGroup
|
||||
title="Brand Primary"
|
||||
colorName="brand-primary"
|
||||
description="Main brand color for primary actions, links, and brand identity elements."
|
||||
colors={theme.colors['brand-primary']}
|
||||
/>
|
||||
|
||||
<ColorGroup
|
||||
title="Brand Secondary"
|
||||
colorName="brand-secondary"
|
||||
description="Complementary color for secondary UI elements and visual hierarchy."
|
||||
colors={theme.colors['brand-secondary']}
|
||||
/>
|
||||
|
||||
<ColorGroup
|
||||
title="Brand Accent"
|
||||
colorName="brand-accent"
|
||||
description="Accent color for call-to-actions and highlighting important elements."
|
||||
colors={theme.colors['brand-accent']}
|
||||
/>
|
||||
|
||||
<ColorGroup
|
||||
title="Brand Gray"
|
||||
colorName="brand-gray"
|
||||
description="Neutral colors for text, backgrounds, borders, and non-emphasized UI elements."
|
||||
colors={theme.colors['brand-gray']}
|
||||
/>
|
||||
|
||||
<ColorGroup
|
||||
title="Success"
|
||||
colorName="success"
|
||||
description="Used for positive actions, success messages, and confirmations."
|
||||
colors={theme.colors.success}
|
||||
/>
|
||||
|
||||
<ColorGroup
|
||||
title="Warning"
|
||||
colorName="warning"
|
||||
description="Used for warnings and alerts that need attention but aren't critical."
|
||||
colors={theme.colors.warning}
|
||||
/>
|
||||
|
||||
<ColorGroup
|
||||
title="Error"
|
||||
colorName="error"
|
||||
description="Used for errors, destructive actions, and critical alerts."
|
||||
colors={theme.colors.error}
|
||||
/>
|
||||
|
||||
<Box my={20}>
|
||||
<Title order={2}>Semantic Color Usage</Title>
|
||||
<Text mb={10}>These are recommended usages for our color system:</Text>
|
||||
|
||||
<Stack gap="xs">
|
||||
<Text>
|
||||
<b>Primary Buttons:</b> brand-primary-6
|
||||
</Text>
|
||||
<Text>
|
||||
<b>Secondary Buttons:</b> brand-secondary-6
|
||||
</Text>
|
||||
<Text>
|
||||
<b>Accent Buttons:</b> brand-accent-6
|
||||
</Text>
|
||||
<Text>
|
||||
<b>Text on Light Backgrounds:</b> brand-gray-7
|
||||
</Text>
|
||||
<Text>
|
||||
<b>Text on Dark Backgrounds:</b> brand-gray-0
|
||||
</Text>
|
||||
<Text>
|
||||
<b>Borders & Dividers:</b> brand-gray-3
|
||||
</Text>
|
||||
<Text>
|
||||
<b>Page Backgrounds:</b> brand-gray-0
|
||||
</Text>
|
||||
<Text>
|
||||
<b>Success States:</b> success-6
|
||||
</Text>
|
||||
<Text>
|
||||
<b>Warning States:</b> warning-6
|
||||
</Text>
|
||||
<Text>
|
||||
<b>Error States:</b> error-6
|
||||
</Text>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
@@ -1,12 +1,63 @@
|
||||
.wrapper {
|
||||
padding: var(--mantine-spacing-xl);
|
||||
background-color: light-dark(var(--mantine-color-white), var(--mantine-color-dark-8));
|
||||
border-radius: var(--mantine-radius-md);
|
||||
box-shadow: var(--mantine-shadow-lg);
|
||||
margin-top: 20px;
|
||||
}
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
background-color: light-dark(var(--mantine-color-white), var(--mantine-color-dark-8));
|
||||
}
|
||||
|
||||
.inner {
|
||||
position: relative;
|
||||
padding-top: 120px;
|
||||
padding-bottom: 120px;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
/* Small screen styles */
|
||||
@media (max-width: 755px) {
|
||||
.inner {
|
||||
padding-bottom: 80px;
|
||||
padding-top: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-family:
|
||||
"Greycliff CF",
|
||||
var(--mantine-font-family);
|
||||
font-size: 62px;
|
||||
font-weight: 900;
|
||||
line-height: 1.1;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: light-dark(var(--mantine-color-black), var(--mantine-color-white));
|
||||
}
|
||||
|
||||
/* Small screen styles */
|
||||
@media (max-width: 755px) {
|
||||
.title {
|
||||
font-size: 42px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-top: var(--mantine-spacing-xl);
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
/* Small screen styles */
|
||||
@media (max-width: 755px) {
|
||||
.description {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.link {
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@@ -1,25 +1,40 @@
|
||||
import { Anchor, Container, Stack, Text, Title } from '@mantine/core';
|
||||
import { Anchor, Container, Stack, Text } from '@mantine/core';
|
||||
import classes from './Contact.module.css';
|
||||
|
||||
export function Contact() {
|
||||
return (
|
||||
<Container className={classes.wrapper}>
|
||||
<Stack gap="md">
|
||||
<Title className={classes.title}>Get in Touch</Title>
|
||||
<Text className={classes.description}>
|
||||
<div className={classes.wrapper}>
|
||||
<Container fluid size={700} className={classes.inner}>
|
||||
<h1 className={classes.title}>
|
||||
<Text component="span" variant="gradient" gradient={{ from: 'blue', to: 'cyan' }} inherit>
|
||||
Get in Touch
|
||||
</Text>
|
||||
</h1>
|
||||
|
||||
<Text className={classes.description} c="dimmed">
|
||||
If you'd like to collaborate, have any questions, or just want to say hello, feel free to
|
||||
reach out!
|
||||
</Text>
|
||||
<Text>
|
||||
Email: <Anchor href="mailto:cdmacfadyen@proton.me">cdmacfadyen@proton.me</Anchor>
|
||||
</Text>
|
||||
<Text>
|
||||
LinkedIn:{' '}
|
||||
<Anchor href="https://www.linkedin.com/in/craig-macfadyen-9a2041197" target="_blank">
|
||||
linkedin.com/in/craigmacfadyen
|
||||
</Anchor>
|
||||
</Text>
|
||||
</Stack>
|
||||
</Container>
|
||||
|
||||
<Stack gap="lg" mt="xl">
|
||||
<Text size="lg">
|
||||
Email:{' '}
|
||||
<Anchor href="mailto:cdmacfadyen@proton.me" className={classes.link}>
|
||||
cdmacfadyen@proton.me
|
||||
</Anchor>
|
||||
</Text>
|
||||
<Text size="lg">
|
||||
LinkedIn:{' '}
|
||||
<Anchor
|
||||
href="https://www.linkedin.com/in/craig-macfadyen-9a2041197"
|
||||
target="_blank"
|
||||
className={classes.link}
|
||||
>
|
||||
linkedin.com/in/craigmacfadyen
|
||||
</Anchor>
|
||||
</Text>
|
||||
</Stack>
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
.wrapper {
|
||||
min-height: 400px;
|
||||
background-image: linear-gradient(
|
||||
-60deg,
|
||||
var(--mantine-color-blue-4) 0%,
|
||||
var(--mantine-color-blue-7) 100%
|
||||
);
|
||||
border-radius: var(--mantine-radius-md);
|
||||
padding: calc(var(--mantine-spacing-xl) * 2.5);
|
||||
|
||||
@media (max-width: $mantine-breakpoint-sm) {
|
||||
padding: calc(var(--mantine-spacing-xl) * 1.5);
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-family:
|
||||
"Greycliff CF",
|
||||
var(--mantine-font-family);
|
||||
color: var(--mantine-color-white);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: var(--mantine-color-blue-0);
|
||||
max-width: 300px;
|
||||
|
||||
@media (max-width: $mantine-breakpoint-sm) {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.form {
|
||||
background-color: var(--mantine-color-white);
|
||||
padding: var(--mantine-spacing-xl);
|
||||
border-radius: var(--mantine-radius-md);
|
||||
box-shadow: var(--mantine-shadow-lg);
|
||||
}
|
||||
|
||||
.social {
|
||||
color: var(--mantine-color-white);
|
||||
|
||||
@mixin hover {
|
||||
color: var(--mantine-color-blue-1);
|
||||
}
|
||||
}
|
||||
|
||||
.input {
|
||||
background-color: var(--mantine-color-white);
|
||||
border-color: var(--mantine-color-gray-4);
|
||||
color: var(--mantine-color-black);
|
||||
|
||||
&::placeholder {
|
||||
color: var(--mantine-color-gray-5);
|
||||
}
|
||||
}
|
||||
|
||||
.inputLabel {
|
||||
color: var(--mantine-color-black);
|
||||
}
|
||||
|
||||
.control {
|
||||
background-color: var(--mantine-color-blue-6);
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
import { TbBrandInstagram, TbBrandTwitter, TbBrandYoutube } from 'react-icons/tb';
|
||||
import {
|
||||
ActionIcon,
|
||||
Button,
|
||||
Group,
|
||||
SimpleGrid,
|
||||
Text,
|
||||
Textarea,
|
||||
TextInput,
|
||||
Title,
|
||||
} from '@mantine/core';
|
||||
import { ContactIconsList } from './ContactIcons';
|
||||
import classes from './ContactUs.module.css';
|
||||
|
||||
const social = [TbBrandInstagram, TbBrandTwitter, TbBrandYoutube];
|
||||
|
||||
export function ContactUs() {
|
||||
const icons = social.map((Icon, index) => (
|
||||
<ActionIcon key={index} size={28} className={classes.social} variant="transparent">
|
||||
<Icon size={22} stroke="1.5" />
|
||||
</ActionIcon>
|
||||
));
|
||||
|
||||
return (
|
||||
<div className={classes.wrapper}>
|
||||
<SimpleGrid cols={{ base: 1, sm: 2 }} spacing={50}>
|
||||
<div>
|
||||
<Title className={classes.title}>Contact us</Title>
|
||||
<Text className={classes.description} mt="sm" mb={30}>
|
||||
Get in touch
|
||||
</Text>
|
||||
|
||||
<ContactIconsList />
|
||||
|
||||
<Group mt="xl">{icons}</Group>
|
||||
</div>
|
||||
<div className={classes.form}>
|
||||
<TextInput
|
||||
label="Email"
|
||||
placeholder="your@email.com"
|
||||
required
|
||||
classNames={{ input: classes.input, label: classes.inputLabel }}
|
||||
/>
|
||||
<TextInput
|
||||
label="Name"
|
||||
placeholder="John Doe"
|
||||
mt="md"
|
||||
classNames={{ input: classes.input, label: classes.inputLabel }}
|
||||
/>
|
||||
<Textarea
|
||||
required
|
||||
label="Your message"
|
||||
placeholder="I want to order your goods"
|
||||
minRows={4}
|
||||
mt="md"
|
||||
classNames={{ input: classes.input, label: classes.inputLabel }}
|
||||
/>
|
||||
|
||||
<Group justify="flex-end" mt="md">
|
||||
<Button className={classes.control}>Send message</Button>
|
||||
</Group>
|
||||
</div>
|
||||
</SimpleGrid>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
.section {
|
||||
margin-top: 80px;
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Carousel } from '@mantine/carousel';
|
||||
import { Container, Grid, Image, Stack, Text, Title } from '@mantine/core';
|
||||
import classes from './ExpertiseSection.module.css';
|
||||
|
||||
interface ExpertiseSectionProps {
|
||||
title: string;
|
||||
content: string;
|
||||
images: { url: string; alt: string }[];
|
||||
viewMoreLink: string;
|
||||
imageOnLeft?: boolean;
|
||||
}
|
||||
|
||||
const ExpertiseSection: React.FC<ExpertiseSectionProps> = ({
|
||||
title,
|
||||
content,
|
||||
images,
|
||||
viewMoreLink: _viewMoreLink,
|
||||
imageOnLeft = false,
|
||||
}) => {
|
||||
const slides = images.map((image) => (
|
||||
<Carousel.Slide key={image.url}>
|
||||
<Image
|
||||
src={image.url}
|
||||
alt={image.alt}
|
||||
style={{ width: '100%', height: 'auto', objectFit: 'cover' }}
|
||||
/>
|
||||
</Carousel.Slide>
|
||||
));
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Grid className={classes.section}>
|
||||
<Grid.Col order={imageOnLeft ? 2 : 1} span={{ xs: 6, md: 8 }}>
|
||||
<Stack>
|
||||
<Title>{title}</Title>
|
||||
<Text size="lg">{content}</Text>
|
||||
{/* <Button component="a" href={viewMoreLink}>
|
||||
View More
|
||||
</Button> */}
|
||||
</Stack>
|
||||
</Grid.Col>
|
||||
<Grid.Col order={imageOnLeft ? 1 : 2} span={{ xs: 6, md: 4 }}>
|
||||
<Carousel
|
||||
slideSize={300}
|
||||
align="start"
|
||||
slideGap="md"
|
||||
controlsOffset="xl"
|
||||
controlSize={28}
|
||||
loop
|
||||
withIndicators
|
||||
>
|
||||
{slides}
|
||||
</Carousel>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default ExpertiseSection;
|
||||
@@ -0,0 +1,39 @@
|
||||
.title {
|
||||
font-size: 34px;
|
||||
font-weight: 900;
|
||||
|
||||
@media (max-width: $mantine-breakpoint-sm) {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.description {
|
||||
max-width: 600px;
|
||||
margin: auto;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
background-color: var(--mantine-color-blue-filled);
|
||||
width: 45px;
|
||||
height: 2px;
|
||||
margin-top: var(--mantine-spacing-sm);
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
border: 1px solid light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-5));
|
||||
}
|
||||
|
||||
.cardTitle {
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
background-color: var(--mantine-color-blue-filled);
|
||||
width: 45px;
|
||||
height: 2px;
|
||||
margin-top: var(--mantine-spacing-sm);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import { FaChartLine, FaCode, FaEye, FaLightbulb, FaMessage, FaShield } from 'react-icons/fa6';
|
||||
import {
|
||||
Badge,
|
||||
Card,
|
||||
Container,
|
||||
Group,
|
||||
SimpleGrid,
|
||||
Text,
|
||||
Title,
|
||||
useMantineTheme,
|
||||
} from '@mantine/core';
|
||||
import classes from './FeatureCards.module.css';
|
||||
|
||||
const mockdata = [
|
||||
{
|
||||
title: 'Computer Vision',
|
||||
description:
|
||||
'I have experience in building computer vision models for a range of applications, from object detection to image segmentation. If your business works with images, these models can improve your efficiency and accuracy.',
|
||||
icon: FaEye,
|
||||
},
|
||||
{
|
||||
title: 'Natural Language Processing',
|
||||
description:
|
||||
'Expertise working with LLMs, from training to deployment. These state-of-the-art models are a powerful tool for a range of applications, and can drive your business forward.',
|
||||
icon: FaMessage,
|
||||
},
|
||||
{
|
||||
title: 'Data Science',
|
||||
description:
|
||||
'Sometimes you just need a simple model and some nice graphs. I can help you understand your data and build a model that works for you.',
|
||||
icon: FaChartLine,
|
||||
},
|
||||
{
|
||||
title: 'Privacy Focused',
|
||||
description:
|
||||
'Your data is valuable and sensitive. All my solutions are built with privacy and security in mind from the ground up, ensuring regulatory compliance and protecting your competitive advantage.',
|
||||
icon: FaShield,
|
||||
},
|
||||
{
|
||||
title: 'Open Source',
|
||||
description:
|
||||
'Leverage the power and innovation of open source technologies while avoiding vendor lock-in. This approach provides transparency, cost-effectiveness, and ensures your solutions remain maintainable long-term.',
|
||||
icon: FaCode,
|
||||
},
|
||||
{
|
||||
title: 'Cutting-Edge Research',
|
||||
description:
|
||||
'I stay continuously updated with the latest advancements in AI research and immediately apply emerging techniques to solve business problems. Your solutions will never be outdated or obsolete.',
|
||||
icon: FaLightbulb,
|
||||
},
|
||||
];
|
||||
|
||||
export function FeaturesCards() {
|
||||
const theme = useMantineTheme();
|
||||
const features = mockdata.map((feature) => (
|
||||
<Card key={feature.title} shadow="md" radius="md" className={classes.card} padding="xl">
|
||||
<feature.icon size={50} color={theme.colors.blue[6]} />
|
||||
<Text fz="lg" fw={500} className={classes.cardTitle} mt="md">
|
||||
{feature.title}
|
||||
</Text>
|
||||
<Text fz="sm" c="dimmed" mt="sm">
|
||||
{feature.description}
|
||||
</Text>
|
||||
</Card>
|
||||
));
|
||||
|
||||
return (
|
||||
<Container size="lg" py="xl">
|
||||
<Group justify="center">
|
||||
<Badge variant="filled" size="lg">
|
||||
My Expertise
|
||||
</Badge>
|
||||
</Group>
|
||||
|
||||
<Title order={2} className={classes.title} ta="center" mt="sm">
|
||||
What can I bring to your business?
|
||||
</Title>
|
||||
|
||||
<Text c="dimmed" className={classes.description} ta="center" mt="md">
|
||||
I've worked with a range of businesses, from startups to established companies, and I've
|
||||
learned a lot about what works and what doesn't. I work across the full Machine Learning
|
||||
stack, from data collection to model deployment.
|
||||
</Text>
|
||||
|
||||
<SimpleGrid cols={{ base: 1, md: 3 }} spacing="xl" mt="50px">
|
||||
{features}
|
||||
</SimpleGrid>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
.footer {
|
||||
margin-top: 120px;
|
||||
border-top: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));
|
||||
}
|
||||
|
||||
.inner {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-top: var(--mantine-spacing-xl);
|
||||
padding-bottom: var(--mantine-spacing-xl);
|
||||
|
||||
@media (max-width: $mantine-breakpoint-xs) {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.links {
|
||||
@media (max-width: $mantine-breakpoint-xs) {
|
||||
margin-top: var(--mantine-spacing-md);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { FaGithub, FaLinkedin } from 'react-icons/fa';
|
||||
import { SiGitea } from 'react-icons/si';
|
||||
import { ActionIcon, Container, Group, Text } from '@mantine/core';
|
||||
import classes from './FooterSocial.module.css';
|
||||
|
||||
export function FooterSocial() {
|
||||
return (
|
||||
<div className={classes.footer}>
|
||||
<Container className={classes.inner}>
|
||||
<Text>Craig Macfadyen</Text>
|
||||
<Group gap={0} className={classes.links} justify="flex-end" wrap="nowrap">
|
||||
<ActionIcon
|
||||
component="a"
|
||||
target="_blank"
|
||||
href="https://github.com/cdmacfadyen"
|
||||
size="lg"
|
||||
color="gray"
|
||||
variant="subtle"
|
||||
>
|
||||
<FaGithub size={18} />
|
||||
</ActionIcon>
|
||||
<ActionIcon
|
||||
component="a"
|
||||
target="_blank"
|
||||
href="https://gitea.craigmacfadyen.co.uk"
|
||||
size="lg"
|
||||
color="gray"
|
||||
variant="subtle"
|
||||
>
|
||||
<SiGitea size={18} />
|
||||
</ActionIcon>
|
||||
<ActionIcon
|
||||
component="a"
|
||||
target="_blank"
|
||||
href="https://www.linkedin.com/in/craig-macfadyen-9a2041197/"
|
||||
size="lg"
|
||||
color="gray"
|
||||
variant="subtle"
|
||||
>
|
||||
<FaLinkedin size={18} />
|
||||
</ActionIcon>
|
||||
</Group>
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
.header {
|
||||
height: 56px;
|
||||
background-color: var(--mantine-color-body);
|
||||
border-bottom: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
||||
background-color: var(--mantine-other-headerBgColor);
|
||||
border-bottom: 1px solid var(--mantine-other-headerBorderColor);
|
||||
}
|
||||
|
||||
.inner {
|
||||
@@ -17,16 +17,16 @@
|
||||
padding: 8px 12px;
|
||||
border-radius: var(--mantine-radius-sm);
|
||||
text-decoration: none;
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0));
|
||||
color: light-dark(var(--mantine-color-brand-gray-7), var(--mantine-color-brand-gray-0));
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
font-weight: 500;
|
||||
|
||||
@mixin hover {
|
||||
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6));
|
||||
background-color: light-dark(var(--mantine-color-brand-gray-1), var(--mantine-color-brand-gray-8));
|
||||
}
|
||||
|
||||
[data-mantine-color-scheme] &[data-active] {
|
||||
background-color: var(--mantine-color-blue-filled);
|
||||
background-color: var(--mantine-other-navigationActiveColor);
|
||||
color: var(--mantine-color-white);
|
||||
}
|
||||
}
|
||||
@@ -1,42 +1,24 @@
|
||||
import { useState } from 'react';
|
||||
import { Burger, Container, Group } from '@mantine/core';
|
||||
import { Burger, Container, Group, Text } from '@mantine/core';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
import { MantineLogo } from '@mantinex/mantine-logo';
|
||||
import { ColorSchemeToggle } from '../ColorSchemeToggle/ColorSchemeToggle';
|
||||
import classes from './HeaderSimple.module.css';
|
||||
|
||||
const links = [
|
||||
{ link: '/about', label: 'Features' },
|
||||
{ link: '/pricing', label: 'Pricing' },
|
||||
{ link: '/learn', label: 'Learn' },
|
||||
{ link: '/community', label: 'Community' },
|
||||
];
|
||||
|
||||
export function HeaderSimple() {
|
||||
const [opened, { toggle }] = useDisclosure(false);
|
||||
const [active, setActive] = useState(links[0].link);
|
||||
|
||||
const items = links.map((link) => (
|
||||
<a
|
||||
key={link.label}
|
||||
href={link.link}
|
||||
className={classes.link}
|
||||
data-active={active === link.link || undefined}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
setActive(link.link);
|
||||
}}
|
||||
>
|
||||
{link.label}
|
||||
</a>
|
||||
));
|
||||
|
||||
return (
|
||||
<header className={classes.header}>
|
||||
<Container size="md" className={classes.inner}>
|
||||
<MantineLogo size={28} />
|
||||
<Text fw={700} size="lg" style={{ userSelect: 'none', cursor: 'default' }}>
|
||||
Craig Macfadyen
|
||||
</Text>
|
||||
<Group gap={5} visibleFrom="xs">
|
||||
{items}
|
||||
{/* <Anchor href="#" className={classes.link}>
|
||||
CV
|
||||
</Anchor>
|
||||
<Anchor href="#" className={classes.link}>
|
||||
Blog
|
||||
</Anchor> */}
|
||||
</Group>
|
||||
|
||||
<Burger opened={opened} onClick={toggle} hiddenFrom="xs" size="sm" />
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
.inner {
|
||||
position: relative;
|
||||
padding-top: 200px;
|
||||
padding-top: 120px;
|
||||
padding-bottom: 120px;
|
||||
width: 80%;
|
||||
|
||||
@@ -41,24 +41,3 @@
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.controls {
|
||||
margin-top: calc(var(--mantine-spacing-xl) * 2);
|
||||
|
||||
@media (max-width: $mantine-breakpoint-sm) {
|
||||
margin-top: var(--mantine-spacing-xl);
|
||||
}
|
||||
}
|
||||
|
||||
.control {
|
||||
height: 54px;
|
||||
padding-left: 38px;
|
||||
padding-right: 38px;
|
||||
|
||||
@media (max-width: $mantine-breakpoint-sm) {
|
||||
height: 54px;
|
||||
padding-left: 18px;
|
||||
padding-right: 18px;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Button, Container, Group, Text } from '@mantine/core';
|
||||
import { Container, Text } from '@mantine/core';
|
||||
// import { GithubIcon } from '@mantinex/dev-icons';
|
||||
import classes from './HeroTitle.module.css';
|
||||
|
||||
@@ -14,32 +14,10 @@ export function HeroTitle() {
|
||||
</h1>
|
||||
|
||||
<Text className={classes.description} c="dimmed">
|
||||
Data Scientist with experience in consulting and research. Expertise in Computer Vision
|
||||
and Large Language Models. Take a look at what I can do for your business and get in touch
|
||||
if you have any questions or you'd like to work together!
|
||||
Machine Learning Engineer with experience in consulting and research. Expertise in
|
||||
Computer Vision and Large Language Models. Take a look at what I can do for your business
|
||||
and get in touch if you have any questions or you'd like to work together!
|
||||
</Text>
|
||||
|
||||
<Group className={classes.controls}>
|
||||
<Button
|
||||
size="xl"
|
||||
className={classes.control}
|
||||
variant="gradient"
|
||||
gradient={{ from: 'blue', to: 'cyan' }}
|
||||
>
|
||||
Get started
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
component="a"
|
||||
href="https://github.com/mantinedev/mantine"
|
||||
size="xl"
|
||||
variant="default"
|
||||
className={classes.control}
|
||||
// leftSection={<GithubIcon size={20} />}
|
||||
>
|
||||
GitHub
|
||||
</Button>
|
||||
</Group>
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,3 +2,7 @@
|
||||
padding-left: 4rem; /* Add more padding to the left */
|
||||
padding-right: 4rem; /* Add more padding to the right */
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
margin-bottom: 80px;
|
||||
}
|
||||
@@ -27,7 +27,7 @@ function TestimonialsCarousel({ testimonials = defaultTestimonials }: Testimonia
|
||||
const autoplay = Autoplay();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.wrapper}>
|
||||
<Title ta="center" mb="xl">
|
||||
Testimonials
|
||||
</Title>
|
||||
@@ -62,7 +62,7 @@ function TestimonialsCarousel({ testimonials = defaultTestimonials }: Testimonia
|
||||
</Carousel.Slide>
|
||||
))}
|
||||
</Carousel>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Container } from '@mantine/core';
|
||||
import openWebUIGif from '@/assets/open-webui.gif';
|
||||
import outputGif from '@/assets/output.gif';
|
||||
import { Contact } from '@/components/ContactUs/Contact';
|
||||
import ExpertiseSection from '@/components/ExpertiseSection/ExpertiseSection';
|
||||
import { FeaturesCards } from '@/components/FeatureCards/FeatureCards';
|
||||
import { FooterSocial } from '@/components/FooterSocial/FooterSocial';
|
||||
import { HeaderSimple } from '@/components/HeaderSimple/HeaderSimple';
|
||||
import { HeroTitle } from '@/components/HeroTitle/HeroTitle';
|
||||
import Testimonial from '@/components/Testimonial/Testimonial';
|
||||
@@ -12,24 +11,12 @@ export function HomePage() {
|
||||
<>
|
||||
<HeaderSimple />
|
||||
<HeroTitle />
|
||||
<ExpertiseSection
|
||||
title="Computer Vision"
|
||||
content="Expertise in developing and deploying computer vision models for various applications, including object detection, image classification, and more. Ask me about state-of-the-art real-time models for your business."
|
||||
images={[{ url: outputGif, alt: 'Video Segmentation with SAM2.' }]}
|
||||
viewMoreLink="/computer-vision"
|
||||
imageOnLeft
|
||||
/>
|
||||
<ExpertiseSection
|
||||
title="Natural Language Processing"
|
||||
content="Building NLP models for tasks such as sentiment analysis, text classification, and language generation. If you need a chatbot for your business and care about privacy, or a simple text classification algorithm to understand your data, I can help."
|
||||
images={[{ url: openWebUIGif, alt: 'Locally hosted LLM using oLLama and OpenWebUI.' }]}
|
||||
viewMoreLink="/nlp"
|
||||
imageOnLeft={false}
|
||||
/>
|
||||
<FeaturesCards />
|
||||
<Container mt={20}>
|
||||
<Testimonial />
|
||||
</Container>
|
||||
<Contact />
|
||||
<FooterSocial />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,140 @@
|
||||
import { createTheme } from '@mantine/core';
|
||||
import { createTheme, rem } from '@mantine/core';
|
||||
|
||||
export const theme = createTheme({
|
||||
/** Put your mantine theme override here */
|
||||
/** Color system for the application */
|
||||
colors: {
|
||||
// Primary brand color (blue shades from inventory with consistent gradation)
|
||||
'brand-primary': [
|
||||
'#EAF4FF', // 0: Lightest shade
|
||||
'#D5E9FF', // 1
|
||||
'#B0D3FF', // 2
|
||||
'#8BBDFF', // 3
|
||||
'#66A7FF', // 4: Base blue shade
|
||||
'#4191FF', // 5
|
||||
'#1C7BFF', // 6
|
||||
'#0065F2', // 7: Original blue-7 shade
|
||||
'#0055CC', // 8
|
||||
'#004099', // 9: Darkest shade
|
||||
],
|
||||
|
||||
// Secondary brand color - a complementary shade to the primary
|
||||
'brand-secondary': [
|
||||
'#F0F4F9', // 0
|
||||
'#E1E9F4', // 1
|
||||
'#C3D4E9', // 2
|
||||
'#A5BFDE', // 3
|
||||
'#87AAD3', // 4
|
||||
'#6995C8', // 5
|
||||
'#4B80BD', // 6
|
||||
'#2D6BB2', // 7
|
||||
'#1F5A99', // 8
|
||||
'#114480', // 9
|
||||
],
|
||||
|
||||
// Accent color for highlights, call to actions, important elements
|
||||
'brand-accent': [
|
||||
'#FFF0EA', // 0
|
||||
'#FFE1D5', // 1
|
||||
'#FFC3AD', // 2
|
||||
'#FFA584', // 3
|
||||
'#FF875B', // 4
|
||||
'#FF6932', // 5
|
||||
'#FF4A09', // 6
|
||||
'#D93A00', // 7
|
||||
'#B02F00', // 8
|
||||
'#882400', // 9
|
||||
],
|
||||
|
||||
// Standardized gray shades for consistency
|
||||
'brand-gray': [
|
||||
'#F8F9FA', // 0
|
||||
'#F1F3F5', // 1
|
||||
'#E9ECEF', // 2
|
||||
'#DEE2E6', // 3
|
||||
'#CED4DA', // 4
|
||||
'#ADB5BD', // 5
|
||||
'#868E96', // 6
|
||||
'#495057', // 7
|
||||
'#343A40', // 8
|
||||
'#212529', // 9
|
||||
],
|
||||
|
||||
// Semantic colors for state indications
|
||||
success: [
|
||||
'#EAFAF1', // 0
|
||||
'#D5F5E3', // 1
|
||||
'#ABEBC6', // 2
|
||||
'#82E0AA', // 3
|
||||
'#58D68D', // 4
|
||||
'#2ECC71', // 5
|
||||
'#27AE60', // 6
|
||||
'#229954', // 7
|
||||
'#1E8449', // 8
|
||||
'#196F3D', // 9
|
||||
],
|
||||
|
||||
warning: [
|
||||
'#FEF9E7', // 0
|
||||
'#FCF3CF', // 1
|
||||
'#F9E79F', // 2
|
||||
'#F7DC6F', // 3
|
||||
'#F4D03F', // 4
|
||||
'#F1C40F', // 5
|
||||
'#D4AC0D', // 6
|
||||
'#B7950B', // 7
|
||||
'#9A7D0A', // 8
|
||||
'#7D6608', // 9
|
||||
],
|
||||
|
||||
error: [
|
||||
'#FDEDEC', // 0
|
||||
'#FADBD8', // 1
|
||||
'#F5B7B1', // 2
|
||||
'#F1948A', // 3
|
||||
'#EC7063', // 4
|
||||
'#E74C3C', // 5
|
||||
'#CB4335', // 6
|
||||
'#B03A2E', // 7
|
||||
'#943126', // 8
|
||||
'#78281F', // 9
|
||||
],
|
||||
},
|
||||
|
||||
// Set the primary color to our brand-primary
|
||||
primaryColor: 'brand-primary',
|
||||
|
||||
// Add consistent spacing values
|
||||
spacing: {
|
||||
xs: rem(4),
|
||||
sm: rem(8),
|
||||
md: rem(16),
|
||||
lg: rem(24),
|
||||
xl: rem(32),
|
||||
'2xl': rem(40),
|
||||
'3xl': rem(64),
|
||||
},
|
||||
|
||||
// Add consistent border radius values
|
||||
radius: {
|
||||
xs: rem(2),
|
||||
sm: rem(4),
|
||||
md: rem(8),
|
||||
lg: rem(16),
|
||||
xl: rem(24),
|
||||
},
|
||||
|
||||
// Other theme customizations
|
||||
fontFamily:
|
||||
'"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
|
||||
|
||||
// Add semantic tokens for common elements
|
||||
other: {
|
||||
headerBgColor: 'var(--mantine-color-body)',
|
||||
headerBorderColor: 'var(--mantine-color-brand-gray-3)',
|
||||
navigationActiveColor: 'var(--mantine-color-brand-primary-6)',
|
||||
navigationHoverColor: 'var(--mantine-color-brand-primary-0)',
|
||||
buttonPrimaryBg: 'var(--mantine-color-brand-primary-6)',
|
||||
buttonSecondaryBg: 'var(--mantine-color-brand-secondary-6)',
|
||||
buttonAccentBg: 'var(--mantine-color-brand-accent-6)',
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user