Initial commit

This commit is contained in:
2026-04-13 17:39:35 +03:00
commit 1a866e376d
147 changed files with 12457 additions and 0 deletions

4
.env Normal file
View File

@@ -0,0 +1,4 @@
NEXT_PUBLIC_API_URL=https://dev.api.webild.io
NEXT_PUBLIC_PROJECT_ID=fda0c3df-5eda-4b44-ab10-e1fc87d8da07

View File

@@ -0,0 +1,46 @@
name: Code Check
on:
workflow_dispatch:
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Checkout
run: git clone --depth 1 --branch ${{ gitea.ref_name }} ${{ gitea.server_url }}/${{ gitea.repository }}.git . || exit 1
- name: Install dependencies
run: |
if [ -f "vite.config.ts" ]; then
if [ -d "/var/node_modules_cache_v4/node_modules" ]; then
ln -s /var/node_modules_cache_v4/node_modules ./node_modules
elif [ -f "pnpm-lock.yaml" ]; then
npm install -g pnpm --silent
pnpm install --frozen-lockfile
else
npm ci --prefer-offline --no-audit
fi
elif [ -d "/var/node_modules_cache/node_modules" ]; then
ln -s /var/node_modules_cache/node_modules ./node_modules
else
npm ci --prefer-offline --no-audit
fi
timeout-minutes: 5
- name: TypeScript check
run: npm run typecheck 2>&1 | tee build.log
timeout-minutes: 3
- name: ESLint check
run: npm run lint 2>&1 | tee -a build.log
timeout-minutes: 3
- name: Upload build log on failure
if: failure()
uses: actions/upload-artifact@v3
with:
name: build-log
path: build.log
retention-days: 1

24
.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

73
README.md Normal file
View File

@@ -0,0 +1,73 @@
# React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)
## React Compiler
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
```js
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Remove tseslint.configs.recommended and replace with this
tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
tseslint.configs.stylisticTypeChecked,
// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```

218
STRUCTURE.md Normal file
View File

@@ -0,0 +1,218 @@
# AI-Friendly Component Library
## The Problem
Current architecture is optimized for human developers (DRY, reusable, flexible) but this makes AI editing fail. AI can't trace through ThemeProvider indirection, 40+ prop interfaces, nested component layers, and custom Tailwind scales. It hallucinates because it can't see the actual output.
## The Solution
Shift from human-optimized to AI-optimized code:
- **Explicit over implicit** - All styles visible in file, no ThemeProvider magic
- **Flat over nested** - Direct components, no wrapper layers to trace through
- **Concrete over abstract** - Actual Tailwind classes, not prop names that map to hidden styles
- **Standard over custom** - Default Tailwind values AI already knows
- **Simple over flexible** - One way to do things, not 11 variants
Components become templates that AI reads, understands, and edits directly.
---
## 1. Backend-Driven Sections
Generated website repository only contains what is used, plus general UI components. Section components are stored on the backend and injected when needed, not bundled in the boilerplate.
**Alternative:** If backend approach takes too long, keep all sections in the boilerplate but with the new simplified structure defined below.
---
## 2. Props & Structure
Remove all styling/className props. AI edits classes directly in the component file.
**Option A - Minimal props:** Keep text, links, assets, arrays, icons as props. Backend passes content, AI edits props in page file.
**Option B - No props:** All content hardcoded in section file. Backend injects entire file with content baked in. AI edits section file directly. Maximum explicitness.
**Flat structure:** Sections use direct components (TextAnimation, Button, MediaContent) not wrapper components like TextBox that nest other components inside.
---
## 3. Framer Motion for Element Animations
Replace GSAP (ScrollTrigger, `gsap.context()`, `ctx.revert()`) with Framer Motion `motion` divs for cards, buttons, tags, and other elements. Use `whileInView` for scroll-triggered animations.
---
## 4. Simplified Tailwind & Single CSS File
Remove all custom Tailwind values (spacing, widths, gaps). Use default Tailwind classes.
**Keep:**
- Content width approach (for page width and carousels)
- Fluid font sizes (AI picks bad font sizes otherwise)
**One globals.css file with:**
- Color variables in `:root`
- `--radius` variable, applied globally via `@layer base`
- Card styles
- Button styles
- Utility classes (masks, animations) - consolidated, fewer variants
- Base styling (body defaults, scrollbar, heading font-family)
AI picks from a reference list of available styles and writes them directly to globals.css.
---
## 5. Remove ThemeProvider
Delete the entire ThemeProvider system. No dynamic style injection.
**Card/button styles:** AI chooses on backend, adds one card style, one primary button style, one secondary button style to globals.css.
**Text animations:** Remove TextBox component. Use simple `TextAnimation` component in section files with `text` and `animationType` props.
**Button:** Single `components/ui/Button.tsx` with `text`, `href`, and `className` (e.g. `primary-button` defined in globals.css).
**Content width:** Fixed value in globals.css.
**Text sizing:** Fixed values in globals.css. Future: AI picks from 3 presets on backend and adds to globals.css.
**Background components:** Remove all (aurora, grid, floatingGradient, etc.).
**Fonts:** Keep current logic.
---
## 6. New Folder Structure
```
src/
├── app/ # Pages
├── components/
│ ├── ui/ # Atomic UI components
│ └── [sections] # Section components flat, not nested (injected from backend)
├── hooks/ # Common hooks
├── lib/ # Utilities
└── styles/
└── globals.css # Single CSS file
```
Remove: `providers/themeProvider/`, multiple CSS files, `button/` variants folder, `text/` folder, `background/` folder.
---
## 7. Atomic UI Components
Add simple, single-purpose components to `components/ui/`:
Button, TextAnimation, MediaContent, Toggle, Dropdown, Accordion, Avatar, Badge, Breadcrumb, Calendar, Chart, Checkbox, Form, Input, Label, Tooltip
Expand as needed.
---
## 8. Simplify Complex Components
**Navbar:** Separate navbar components instead of one with variants. AI chooses which one. Reduce to 4 navbars for now.
**CardStack → GridLayout:** Replace complex CardStack (mode, gridVariant, carouselThreshold, ~40 props) with simple wrapper. Children as items. Single prop: items per row (3 or 4). Under = grid, over = carousel. Carousel mode keeps controls (prev/next, progress bar). No title/description/animation props - sections handle all that directly. Remove timelines, grid configs, auto-carousel variants for now, refactor later.
**TextBox:** Remove completely. Use TextAnimation and Button directly in sections.
---
## 9. Consistent File Patterns
Every section file follows the EXACT same structure. AI sees one section, knows how all work.
**Template:**
```
1. IMPORTS (same order: react, framer-motion, ui components, icons)
2. CONTENT (variables at top if Option B)
3. COMPONENT (predictable JSX order)
4. EXPORT (always at bottom)
```
**JSX order:** tag → title → description → buttons → media (always same sequence)
---
## 10. Short Files, No Sub-components
**Line limits:**
- Sections: under 100 lines
- UI components: under 50 lines
**Key rule:** Don't split into sub-components to achieve this. Sub-components add nesting/indirection. Instead, simplify the section itself.
If a section is too long, it's too complex. Simplify the design, don't extract parts.
---
## 11. Content at Top
If using Option B (no props), put all editable content as variables at the very top of the file.
AI immediately knows: "edit content? check the top."
---
## 12. Remove Complex Components
Remove overly complex components for now. Add back simplified versions later if needed.
---
## 13. CSS Class Organization
**Consistent class order:** layout → spacing → sizing → typography → colors → effects
**Keep className short.** Too many classes = section too complex. Use global CSS (`.card`, `.btn-primary`) for repeated patterns.
**No dynamic classes in sections.** Avoid `cls(condition && "class")`. Keep it explicit.
**One line if short, split by category if long.**
---
## Implementation Roadmap
### Phase 1: Foundation (Delete)
- Delete `providers/themeProvider/` (13 files)
- Delete `components/background/` (27 files)
- Delete `components/Textbox.tsx`
- Delete `components/text/` (GSAP TextAnimation)
- Consolidate all CSS → single `styles/globals.css`
### Phase 2: Core Simplification
- Button → single `ui/Button.tsx` (50 lines max)
- Navbar → 4 separate components, no animation hooks
- TextAnimation → new Framer Motion version
- CardStack → simple GridLayout wrapper
### Phase 3: Section Refactoring
- Remove all className/containerClassName props
- Remove useTheme() calls
- Each section under 100 lines
- Content variables at top (if Option B)
- Flat structure, no nested wrappers
### Phase 4: Organize
- Create `components/ui/` with atomic components
- Move hooks to `hooks/` folder
- Update all imports
---
## Current → Target
| Metric | Current | Target |
|--------|---------|--------|
| Total files | ~270 | ~100 |
| Lines of code | ~25,000 | ~10,000 |
| Props per section | 20-40 | 5-10 |
| CSS files | 37 | 1 |
| Button variants | 11 | 1 |
| Navbar variants | 5 | 4 |
---

View File

@@ -0,0 +1,58 @@
================================================================================
THEME OPTIONS (v4)
================================================================================
STYLE PROPS
-----------
1. borderRadius
• "rounded"
• "soft"
• "pill"
2. contentWidth
• "small"
• "compact"
• "medium"
• "mediumLarge"
3. cardStyle
• "solid"
• "outline"
• "gradient-mesh"
• "gradient-radial"
• "inset"
• "glass-elevated"
• "glass-depth"
• "gradient-bordered"
• "layered-gradient"
• "soft-shadow"
• "subtle-shadow"
• "elevated-border"
• "inner-glow"
• "spotlight"
4. primaryButtonStyle
• "gradient"
• "shadow"
• "flat"
• "radial-glow"
• "diagonal-gradient"
• "double-inset"
• "primary-glow"
• "inset-glow"
• "soft-glow"
• "glass-shimmer"
• "neon-outline"
• "lifted"
• "depth-layers"
• "accent-edge"
• "metallic"
5. secondaryButtonStyle
• "glass"
• "solid"
• "layered"
• "radial-glow"
================================================================================

677
colorThemes.json Normal file
View File

@@ -0,0 +1,677 @@
{
"lightTheme": {
"minimalDarkBlue": {
"--background": "#ffffff",
"--card": "#f9f9f9",
"--foreground": "#000612e6",
"--primary-cta": "#15479c",
"--secondary-cta": "#f9f9f9",
"--accent": "#e2e2e2",
"--background-accent": "#c4c4c4",
"--primary-cta-text": "#ffffff",
"--secondary-cta-text": "#000612e6"
},
"minimalDarkGreen": {
"--background": "#ffffff",
"--card": "#f9f9f9",
"--foreground": "#000f06e6",
"--primary-cta": "#0a7039",
"--secondary-cta": "#f9f9f9",
"--accent": "#e2e2e2",
"--background-accent": "#c4c4c4",
"--primary-cta-text": "#ffffff",
"--secondary-cta-text": "#000f06e6"
},
"minimalLightRed": {
"--background": "#ffffff",
"--card": "#f9f9f9",
"--foreground": "#120006e6",
"--primary-cta": "#e63946",
"--secondary-cta": "#f9f9f9",
"--accent": "#e2e2e2",
"--background-accent": "#c4c4c4",
"--primary-cta-text": "#ffffff",
"--secondary-cta-text": "#120006e6"
},
"minimalBrightBlue": {
"--background": "#ffffff",
"--card": "#f9f9f9",
"--foreground": "#000612e6",
"--primary-cta": "#106EFB",
"--secondary-cta": "#f9f9f9",
"--accent": "#e2e2e2",
"--background-accent": "#106EFB",
"--primary-cta-text": "#ffffff",
"--secondary-cta-text": "#000612e6"
},
"minimalBrightOrange": {
"--background": "#ffffff",
"--card": "#f9f9f9",
"--foreground": "#120a00e6",
"--primary-cta": "#E34400",
"--secondary-cta": "#f9f9f9",
"--accent": "#e2e2e2",
"--background-accent": "#E34400",
"--primary-cta-text": "#ffffff",
"--secondary-cta-text": "#120a00e6"
},
"minimalGoldenOrange": {
"--background": "#ffffff",
"--card": "#f9f9f9",
"--foreground": "#120a00e6",
"--primary-cta": "#FF7B05",
"--secondary-cta": "#f9f9f9",
"--accent": "#e2e2e2",
"--background-accent": "#FF7B05",
"--primary-cta-text": "#ffffff",
"--secondary-cta-text": "#120a00e6"
},
"minimalLightOrange": {
"--background": "#ffffff",
"--card": "#f9f9f9",
"--foreground": "#120a00e6",
"--primary-cta": "#ff8c42",
"--secondary-cta": "#f9f9f9",
"--accent": "#e2e2e2",
"--background-accent": "#c4c4c4",
"--primary-cta-text": "#ffffff",
"--secondary-cta-text": "#120a00e6"
},
"darkBlue": {
"--background": "#f5faff",
"--card": "#f1f8ff",
"--foreground": "#001122",
"--primary-cta": "#15479c",
"--secondary-cta": "#ffffff",
"--accent": "#a8cce8",
"--background-accent": "#7ba3cf",
"--primary-cta-text": "#f5faff",
"--secondary-cta-text": "#001122"
},
"darkGreen": {
"--background": "#fafffb",
"--card": "#f7fffa",
"--foreground": "#001a0a",
"--primary-cta": "#0a7039",
"--secondary-cta": "#ffffff",
"--accent": "#a8d9be",
"--background-accent": "#6bbf8e",
"--primary-cta-text": "#fafffb",
"--secondary-cta-text": "#001a0a"
},
"lightRed": {
"--background": "#fffafa",
"--card": "#fff7f7",
"--foreground": "#1a0000",
"--primary-cta": "#e63946",
"--secondary-cta": "#ffffff",
"--accent": "#f5c4c7",
"--background-accent": "#f09199",
"--primary-cta-text": "#fffafa",
"--secondary-cta-text": "#1a0000"
},
"lightPurple": {
"--background": "#fbfaff",
"--card": "#f7f5ff",
"--foreground": "#0f0022",
"--primary-cta": "#8b5cf6",
"--secondary-cta": "#ffffff",
"--accent": "#d8cef5",
"--background-accent": "#c4a8f9",
"--primary-cta-text": "#fbfaff",
"--secondary-cta-text": "#0f0022"
},
"warmCream": {
"--background": "#f6f0e9",
"--card": "#efe7dd",
"--foreground": "#2b180a",
"--primary-cta": "#2b180a",
"--secondary-cta": "#efe7dd",
"--accent": "#94877c",
"--background-accent": "#afa094",
"--primary-cta-text": "#f6f0e9",
"--secondary-cta-text": "#2b180a"
},
"grayBlueAccent": {
"--background": "#f5f5f5",
"--card": "#ffffff",
"--foreground": "#1c1c1c",
"--primary-cta": "#1c1c1c",
"--secondary-cta": "#ffffff",
"--accent": "#15479c",
"--background-accent": "#a8cce8",
"--primary-cta-text": "#f5f5f5",
"--secondary-cta-text": "#1c1c1c"
},
"grayGreenAccent": {
"--background": "#f5f5f5",
"--card": "#ffffff",
"--foreground": "#1c1c1c",
"--primary-cta": "#1c1c1c",
"--secondary-cta": "#ffffff",
"--accent": "#159c49",
"--background-accent": "#a8e8ba",
"--primary-cta-text": "#f5f5f5",
"--secondary-cta-text": "#1c1c1c"
},
"grayRedAccent": {
"--background": "#f5f5f5",
"--card": "#ffffff",
"--foreground": "#1c1c1c",
"--primary-cta": "#1c1c1c",
"--secondary-cta": "#ffffff",
"--accent": "#e63946",
"--background-accent": "#e8bea8",
"--primary-cta-text": "#f5f5f5",
"--secondary-cta-text": "#1c1c1c"
},
"grayPurpleAccent": {
"--background": "#f5f5f5",
"--card": "#ffffff",
"--foreground": "#1c1c1c",
"--primary-cta": "#1c1c1c",
"--secondary-cta": "#ffffff",
"--accent": "#6139e6",
"--background-accent": "#b3a8e8",
"--primary-cta-text": "#f5f5f5",
"--secondary-cta-text": "#1c1c1c"
},
"warmBeige": {
"--background": "#efebe5",
"--card": "#f7f2ea",
"--foreground": "#000000",
"--primary-cta": "#000000",
"--secondary-cta": "#ffffff",
"--accent": "#ffffff",
"--background-accent": "#e1b875",
"--primary-cta-text": "#efebe5",
"--secondary-cta-text": "#000000"
},
"grayTealGreen": {
"--background": "#f5f5f5",
"--card": "#ffffff",
"--foreground": "#1c1c1c",
"--primary-cta": "#1f514c",
"--secondary-cta": "#ffffff",
"--accent": "#159c49",
"--background-accent": "#a8e8ba",
"--primary-cta-text": "#f5f5f5",
"--secondary-cta-text": "#1c1c1c"
},
"grayNavyBlue": {
"--background": "#f5f5f5",
"--card": "#ffffff",
"--foreground": "#1c1c1c",
"--primary-cta": "#1f3251",
"--secondary-cta": "#ffffff",
"--accent": "#15479c",
"--background-accent": "#a8cce8",
"--primary-cta-text": "#f5f5f5",
"--secondary-cta-text": "#1c1c1c"
},
"grayBurgundyRed": {
"--background": "#f5f5f5",
"--card": "#ffffff",
"--foreground": "#1c1c1c",
"--primary-cta": "#511f1f",
"--secondary-cta": "#ffffff",
"--accent": "#e63946",
"--background-accent": "#e8bea8",
"--primary-cta-text": "#f5f5f5",
"--secondary-cta-text": "#1c1c1c"
},
"grayIndigoPurple": {
"--background": "#f5f5f5",
"--card": "#ffffff",
"--foreground": "#1c1c1c",
"--primary-cta": "#341f51",
"--secondary-cta": "#ffffff",
"--accent": "#6139e6",
"--background-accent": "#b3a8e8",
"--primary-cta-text": "#f5f5f5",
"--secondary-cta-text": "#1c1c1c"
},
"warmgrayPink": {
"--background": "#f7f6f7",
"--card": "#ffffff",
"--foreground": "#1b0c25",
"--primary-cta": "#1b0c25",
"--secondary-cta": "#ffffff",
"--accent": "#ff93e4",
"--background-accent": "#e8a8c3",
"--primary-cta-text": "#f7f6f7",
"--secondary-cta-text": "#1b0c25"
},
"warmgrayOrange": {
"--background": "#f7f6f7",
"--card": "#ffffff",
"--foreground": "#25190c",
"--primary-cta": "#ff6207",
"--secondary-cta": "#ffffff",
"--accent": "#ffce93",
"--background-accent": "#e8cfa8",
"--primary-cta-text": "#f7f6f7",
"--secondary-cta-text": "#25190c"
},
"warmgrayBlue": {
"--background": "#f7f6f7",
"--card": "#ffffff",
"--foreground": "#0c1325",
"--primary-cta": "#0798ff",
"--secondary-cta": "#ffffff",
"--accent": "#93c7ff",
"--background-accent": "#a8cde8",
"--primary-cta-text": "#f7f6f7",
"--secondary-cta-text": "#0c1325"
},
"warmgrayIndigo": {
"--background": "#f7f6f7",
"--card": "#ffffff",
"--foreground": "#0c1325",
"--primary-cta": "#0b07ff",
"--secondary-cta": "#ffffff",
"--accent": "#93b7ff",
"--background-accent": "#a8bae8",
"--primary-cta-text": "#f7f6f7",
"--secondary-cta-text": "#0c1325"
},
"lavenderPeach": {
"--background": "#e3deea",
"--card": "#ffffff",
"--foreground": "#27231f",
"--primary-cta": "#27231f",
"--secondary-cta": "#ffffff",
"--accent": "#c68a62",
"--background-accent": "#c68a62",
"--primary-cta-text": "#e3deea",
"--secondary-cta-text": "#27231f"
},
"lavenderBlue": {
"--background": "#e3deea",
"--card": "#ffffff",
"--foreground": "#1f2027",
"--primary-cta": "#1f2027",
"--secondary-cta": "#ffffff",
"--accent": "#627dc6",
"--background-accent": "#627dc6",
"--primary-cta-text": "#e3deea",
"--secondary-cta-text": "#1f2027"
},
"warmStone": {
"--background": "#f5f4ef",
"--card": "#dad6cd",
"--foreground": "#2a2928",
"--primary-cta": "#2a2928",
"--secondary-cta": "#ecebea",
"--accent": "#ffffff",
"--background-accent": "#c6b180",
"--primary-cta-text": "#f5f4ef",
"--secondary-cta-text": "#2a2928"
},
"warmStoneGray": {
"--background": "#f5f4f0",
"--card": "#ffffff",
"--foreground": "#1a1a1a",
"--primary-cta": "#2c2c2c",
"--secondary-cta": "#f5f4f0",
"--accent": "#8a8a8a",
"--background-accent": "#e8e6e1",
"--primary-cta-text": "#f5f4f0",
"--secondary-cta-text": "#1a1a1a"
},
"warmGreen": {
"--background": "#fffefe",
"--card": "#f6f7f4",
"--foreground": "#080908",
"--primary-cta": "#0e3a29",
"--secondary-cta": "#e7eecd",
"--accent": "#35c18b",
"--background-accent": "#ecebe4",
"--primary-cta-text": "#fffefe",
"--secondary-cta-text": "#080908"
},
"warmSand": {
"--background": "#fcf6ec",
"--card": "#f3ede2",
"--foreground": "#2e2521",
"--primary-cta": "#2e2521",
"--secondary-cta": "#ffffff",
"--accent": "#b2a28b",
"--background-accent": "#b2a28b",
"--primary-cta-text": "#fcf6ec",
"--secondary-cta-text": "#2e2521"
},
"warmgrayRed": {
"--background": "#f7f6f7",
"--card": "#ffffff",
"--foreground": "#250c0d",
"--primary-cta": "#b82b40",
"--secondary-cta": "#ffffff",
"--accent": "#b90941",
"--background-accent": "#e8a8b6",
"--primary-cta-text": "#f7f6f7",
"--secondary-cta-text": "#250c0d"
}
},
"darkTheme": {
"minimal": {
"--background": "#0a0a0a",
"--card": "#1a1a1a",
"--foreground": "#ffffffe6",
"--primary-cta": "#e6e6e6",
"--secondary-cta": "#1a1a1a",
"--accent": "#737373",
"--background-accent": "#737373",
"--primary-cta-text": "#0a0a0a",
"--secondary-cta-text": "#ffffffe6"
},
"minimalLightBlue": {
"--background": "#0a0a0a",
"--card": "#1a1a1a",
"--foreground": "#f0f8ffe6",
"--primary-cta": "#cee7ff",
"--secondary-cta": "#1a1a1a",
"--accent": "#737373",
"--background-accent": "#737373",
"--primary-cta-text": "#0a0a0a",
"--secondary-cta-text": "#f0f8ffe6"
},
"minimalLightGreen": {
"--background": "#0a0a0a",
"--card": "#1a1a1a",
"--foreground": "#f5fffae6",
"--primary-cta": "#80da9b",
"--secondary-cta": "#1a1a1a",
"--accent": "#737373",
"--background-accent": "#737373",
"--primary-cta-text": "#0a0a0a",
"--secondary-cta-text": "#f5fffae6"
},
"minimalLightRed": {
"--background": "#0a0a0a",
"--card": "#1a1a1a",
"--foreground": "#fff5f5e6",
"--primary-cta": "#ff7a7a",
"--secondary-cta": "#1a1a1a",
"--accent": "#737373",
"--background-accent": "#737373",
"--primary-cta-text": "#0a0a0a",
"--secondary-cta-text": "#fff5f5e6"
},
"minimalLightPurple": {
"--background": "#0a0a0a",
"--card": "#1a1a1a",
"--foreground": "#f8f5ffe6",
"--primary-cta": "#c89bff",
"--secondary-cta": "#1a1a1a",
"--accent": "#737373",
"--background-accent": "#737373",
"--primary-cta-text": "#0a0a0a",
"--secondary-cta-text": "#f8f5ffe6"
},
"lightBlueWhite": {
"--background": "#010912",
"--card": "#152840",
"--foreground": "#e6f0ff",
"--primary-cta": "#cee7ff",
"--secondary-cta": "#0e1a29",
"--accent": "#3f5c79",
"--background-accent": "#004a93",
"--primary-cta-text": "#010912",
"--secondary-cta-text": "#ffffff"
},
"lime": {
"--background": "#0a0a0a",
"--card": "#1a1a1a",
"--foreground": "#f5f5f5",
"--primary-cta": "#dfff1c",
"--secondary-cta": "#1a1a1a",
"--accent": "#8b9a1b",
"--background-accent": "#5d6b00",
"--primary-cta-text": "#0a0a0a",
"--secondary-cta-text": "#ffffff"
},
"gold": {
"--background": "#0a0a0a",
"--card": "#1a1a1a",
"--foreground": "#f5f5f5",
"--primary-cta": "#ffdf7d",
"--secondary-cta": "#1a1a1a",
"--accent": "#b8860b",
"--background-accent": "#8b6914",
"--primary-cta-text": "#0a0a0a",
"--secondary-cta-text": "#ffffff"
},
"crimson": {
"--background": "#0a0a0a",
"--card": "#1a1a1a",
"--foreground": "#f5f5f5",
"--primary-cta": "#ff0000",
"--secondary-cta": "#1a1a1a",
"--accent": "#991b1b",
"--background-accent": "#7f1d1d",
"--primary-cta-text": "#ffffff",
"--secondary-cta-text": "#ffffff"
},
"midnightIce": {
"--background": "#000000",
"--card": "#0c0c0c",
"--foreground": "#ffffff",
"--primary-cta": "#cee7ff",
"--secondary-cta": "#000000",
"--accent": "#535353",
"--background-accent": "#CEE7FF",
"--primary-cta-text": "#000000",
"--secondary-cta-text": "#ffffff"
},
"midnightBlue": {
"--background": "#000000",
"--card": "#0c0c0c",
"--foreground": "#ffffff",
"--primary-cta": "#106EFB",
"--secondary-cta": "#000000",
"--accent": "#535353",
"--background-accent": "#106EFB",
"--primary-cta-text": "#ffffff",
"--secondary-cta-text": "#ffffff"
},
"blueOrangeAccent": {
"--background": "#0a0a0a",
"--card": "#1a1a1a",
"--foreground": "#ffffff",
"--primary-cta": "#1f7cff",
"--secondary-cta": "#010101",
"--accent": "#1f7cff",
"--background-accent": "#f96b2f",
"--primary-cta-text": "#ffffff",
"--secondary-cta-text": "#ffffff"
},
"orangeBlueAccent": {
"--background": "#0a0a0a",
"--card": "#1a1a1a",
"--foreground": "#ffffff",
"--primary-cta": "#e34400",
"--secondary-cta": "#010101",
"--accent": "#ff7b05",
"--background-accent": "#106efb",
"--primary-cta-text": "#ffffff",
"--secondary-cta-text": "#ffffff"
},
"minimalBrightOrange": {
"--background": "#0a0a0a",
"--card": "#1a1a1a",
"--foreground": "#ffffff",
"--primary-cta": "#e34400",
"--secondary-cta": "#010101",
"--accent": "#737373",
"--background-accent": "#e34400",
"--primary-cta-text": "#ffffff",
"--secondary-cta-text": "#ffffff"
},
"minimalLightOrange": {
"--background": "#0a0a0a",
"--card": "#1a1a1a",
"--foreground": "#fffaf5e6",
"--primary-cta": "#ffaa70",
"--secondary-cta": "#1a1a1a",
"--accent": "#737373",
"--background-accent": "#737373",
"--primary-cta-text": "#0a0a0a",
"--secondary-cta-text": "#fffaf5e6"
},
"minimalLightYellow": {
"--background": "#0a0a0a",
"--card": "#1a1a1a",
"--foreground": "#fffffae6",
"--primary-cta": "#fde047",
"--secondary-cta": "#1a1a1a",
"--accent": "#737373",
"--background-accent": "#737373",
"--primary-cta-text": "#0a0a0a",
"--secondary-cta-text": "#fffffae6"
},
"lightBlue": {
"--background": "#010912",
"--card": "#152840",
"--foreground": "#e6f0ff",
"--primary-cta": "#cee7ff",
"--secondary-cta": "#0e1a29",
"--accent": "#3f5c79",
"--background-accent": "#004a93",
"--primary-cta-text": "#010912",
"--secondary-cta-text": "#e6f0ff"
},
"lightGreen": {
"--background": "#000802",
"--card": "#0b1a0b",
"--foreground": "#e6ffe6",
"--primary-cta": "#80da9b",
"--secondary-cta": "#07170b",
"--accent": "#38714a",
"--background-accent": "#2c6541",
"--primary-cta-text": "#000802",
"--secondary-cta-text": "#e6ffe6"
},
"lightRed": {
"--background": "#080000",
"--card": "#1e0d0d",
"--foreground": "#ffe6e6",
"--primary-cta": "#ff7a7a",
"--secondary-cta": "#1e0909",
"--accent": "#7b4242",
"--background-accent": "#65292c",
"--primary-cta-text": "#080000",
"--secondary-cta-text": "#ffe6e6"
},
"darkRed": {
"--background": "#060000",
"--card": "#1d0d0d",
"--foreground": "#ffe6e6",
"--primary-cta": "#ff3d4a",
"--secondary-cta": "#1f0a0a",
"--accent": "#7b2d2d",
"--background-accent": "#b8111f",
"--primary-cta-text": "#ffffff",
"--secondary-cta-text": "#ffe6e6"
},
"lightPurple": {
"--background": "#050012",
"--card": "#040121",
"--foreground": "#f0e6ff",
"--primary-cta": "#c89bff",
"--secondary-cta": "#1d123b",
"--accent": "#684f7b",
"--background-accent": "#65417c",
"--primary-cta-text": "#050012",
"--secondary-cta-text": "#f0e6ff"
},
"lightOrange": {
"--background": "#080200",
"--card": "#1a0d0b",
"--foreground": "#ffe6d5",
"--primary-cta": "#ffaa70",
"--secondary-cta": "#170b07",
"--accent": "#7b5e4a",
"--background-accent": "#b8541e",
"--primary-cta-text": "#080200",
"--secondary-cta-text": "#ffe6d5"
},
"deepBlue": {
"--background": "#020617",
"--card": "#0f172a",
"--foreground": "#e2e8f0",
"--primary-cta": "#c4d8f9",
"--secondary-cta": "#041633",
"--accent": "#2d30f3",
"--background-accent": "#1d4ed8",
"--primary-cta-text": "#020617",
"--secondary-cta-text": "#e2e8f0"
},
"violet": {
"--background": "#030128",
"--card": "#241f48",
"--foreground": "#ffffff",
"--primary-cta": "#ffffff",
"--secondary-cta": "#131136",
"--accent": "#44358a",
"--background-accent": "#b597fe",
"--primary-cta-text": "#030128",
"--secondary-cta-text": "#d5d4f6"
},
"ruby": {
"--background": "#000000",
"--card": "#481f1f",
"--foreground": "#ffffff",
"--primary-cta": "#ffffff",
"--secondary-cta": "#361311",
"--accent": "#51000b",
"--background-accent": "#ff2231",
"--primary-cta-text": "#280101",
"--secondary-cta-text": "#f6d4d4"
},
"emerald": {
"--background": "#000000",
"--card": "#1f4035",
"--foreground": "#ffffff",
"--primary-cta": "#ffffff",
"--secondary-cta": "#0d2b1f",
"--accent": "#0d5238",
"--background-accent": "#10b981",
"--primary-cta-text": "#051a12",
"--secondary-cta-text": "#d4f6e8"
},
"indigo": {
"--background": "#000000",
"--card": "#1f1f40",
"--foreground": "#ffffff",
"--primary-cta": "#ffffff",
"--secondary-cta": "#0d0d2b",
"--accent": "#3d2880",
"--background-accent": "#663cff",
"--primary-cta-text": "#0a051a",
"--secondary-cta-text": "#d4d4f6"
},
"forest": {
"--background": "#000000",
"--card": "#1a2f1d",
"--foreground": "#ffffff",
"--primary-cta": "#ffffff",
"--secondary-cta": "#0d200f",
"--accent": "#1a3d1f",
"--background-accent": "#355e3b",
"--primary-cta-text": "#0a1a0c",
"--secondary-cta-text": "#d4f6d8"
},
"mint": {
"--background": "#000000",
"--card": "#1a2a1a",
"--foreground": "#ffffff",
"--primary-cta": "#ffffff",
"--secondary-cta": "#0d1a0d",
"--accent": "#2d4a2d",
"--background-accent": "#c1e1c1",
"--primary-cta-text": "#0a150a",
"--secondary-cta-text": "#e1f6e1"
}
}
}

41
cssOptions.json Normal file
View File

@@ -0,0 +1,41 @@
{
"cards": {
"solid": "background: var(--color-card);",
"outline": "background: var(--color-card);\nborder: 1px solid color-mix(in srgb, var(--color-accent) 25%, transparent);",
"gradient-mesh": "background:\n radial-gradient(at 0% 0%, color-mix(in srgb, var(--color-accent) 15%, transparent) 0px, transparent 50%),\n radial-gradient(at 100% 0%, color-mix(in srgb, var(--color-accent) 10%, transparent) 0px, transparent 50%),\n radial-gradient(at 100% 100%, color-mix(in srgb, var(--color-accent) 20%, transparent) 0px, transparent 50%),\n radial-gradient(at 0% 100%, color-mix(in srgb, var(--color-accent) 12%, transparent) 0px, transparent 50%),\n var(--color-card);",
"gradient-radial": "background: radial-gradient(circle at center, color-mix(in srgb, var(--color-card) 100%, var(--color-accent) 20%) 0%, var(--color-card) 90%);",
"inset": "background: color-mix(in srgb, var(--color-card) 95%, var(--color-accent) 5%);\nbox-shadow:\n inset 2px 2px 4px color-mix(in srgb, var(--color-foreground) 8%, transparent),\n inset -2px -2px 4px color-mix(in srgb, var(--color-background) 20%, transparent);",
"glass-elevated": "backdrop-filter: blur(8px);\nbackground: linear-gradient(to bottom right, color-mix(in srgb, var(--color-card) 80%, transparent), color-mix(in srgb, var(--color-card) 40%, transparent));\nbox-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);\nborder: 1px solid var(--color-card);",
"glass-depth": "background: color-mix(in srgb, var(--color-card) 80%, transparent);\nbackdrop-filter: blur(14px);\nbox-shadow:\n inset 0 0 20px 0 color-mix(in srgb, var(--color-accent) 7.5%, transparent);\nborder: 1px solid color-mix(in srgb, var(--color-accent) 7.5%, transparent);",
"gradient-bordered": "background: linear-gradient(180deg, color-mix(in srgb, var(--color-card) 100%, var(--color-accent) 5%) -35%, var(--color-card) 65%);\nbox-shadow: 0px 0px 10px 4px color-mix(in srgb, var(--color-accent) 4%, transparent);\nborder: 1px solid color-mix(in srgb, var(--color-accent) 15%, transparent);",
"layered-gradient": "background:\n linear-gradient(color-mix(in srgb, var(--color-accent) 6%, transparent) 0%, transparent 59.26%),\n linear-gradient(var(--color-card) 0%, var(--color-card) 100%),\n var(--color-card);\nbox-shadow:\n 20px 18px 7px color-mix(in srgb, var(--color-accent) 0%, transparent),\n 2px 2px 2px color-mix(in srgb, var(--color-accent) 6.5%, transparent),\n 1px 1px 2px color-mix(in srgb, var(--color-accent) 2%, transparent);\nborder: 2px solid var(--color-secondary-cta);",
"soft-shadow": "background: var(--color-card);\nbox-shadow: color-mix(in srgb, var(--color-accent) 10%, transparent) 0px 0.706592px 0.706592px -0.666667px, color-mix(in srgb, var(--color-accent) 8%, transparent) 0px 1.80656px 1.80656px -1.33333px, color-mix(in srgb, var(--color-accent) 7%, transparent) 0px 3.62176px 3.62176px -2px, color-mix(in srgb, var(--color-accent) 7%, transparent) 0px 6.8656px 6.8656px -2.66667px, color-mix(in srgb, var(--color-accent) 5%, transparent) 0px 13.6468px 13.6468px -3.33333px, color-mix(in srgb, var(--color-accent) 2%, transparent) 0px 30px 30px -4px, var(--color-background) 0px 3px 1px 0px inset;",
"subtle-shadow": "background: var(--color-card);\nbox-shadow: color-mix(in srgb, var(--color-foreground) 5%, transparent) 0px 4px 32px 0px;",
"elevated-border": "background: linear-gradient(180deg, color-mix(in srgb, var(--color-card) 100%, var(--color-foreground) 3%) 0%, var(--color-card) 100%);\nbox-shadow: 0 1px 0 0 color-mix(in srgb, var(--color-foreground) 8%, transparent), 0 4px 6px -1px color-mix(in srgb, var(--color-foreground) 5%, transparent), 0 10px 15px -3px color-mix(in srgb, var(--color-foreground) 4%, transparent);\nborder: 1px solid color-mix(in srgb, var(--color-foreground) 6%, transparent);",
"inner-glow": "background: var(--color-card);\nbox-shadow: inset 0 0 30px 0 color-mix(in srgb, var(--color-foreground) 4%, transparent), inset 0 1px 0 0 color-mix(in srgb, var(--color-foreground) 8%, transparent), 0 4px 12px -4px color-mix(in srgb, var(--color-foreground) 8%, transparent);",
"spotlight": "background:\n radial-gradient(ellipse at 0% 0%, color-mix(in srgb, var(--color-accent) 20%, transparent) 0%, transparent 50%),\n var(--color-card);\nbox-shadow: inset 1px 1px 0 0 color-mix(in srgb, var(--color-foreground) 10%, transparent), 0 4px 16px -4px color-mix(in srgb, var(--color-foreground) 10%, transparent);"
},
"primaryButtons": {
"gradient": "background: linear-gradient(to bottom, color-mix(in srgb, var(--color-primary-cta) 75%, transparent), var(--color-primary-cta));\nbox-shadow: color-mix(in srgb, var(--color-background) 25%, transparent) 0px 1px 1px 0px inset, color-mix(in srgb, var(--color-primary-cta) 15%, transparent) 3px 3px 3px 0px;",
"shadow": "background: var(--color-primary-cta);\nbox-shadow: 2.10837px 3.16256px 9.48767px color-mix(in srgb, var(--color-primary-cta) 40%, transparent);",
"flat": "background: var(--color-primary-cta);",
"radial-glow": "background:\n radial-gradient(circle at 0% 0%, color-mix(in srgb, var(--color-background) 32.5%, transparent) 0%, transparent 45%),\n radial-gradient(circle at 100% 100%, color-mix(in srgb, var(--color-background) 32.5%, transparent) 0%, transparent 45%),\n var(--color-primary-cta);\nbox-shadow: 2.10837px 3.16256px 9.48767px color-mix(in srgb, var(--color-accent) 30%, transparent);",
"diagonal-gradient": "background: linear-gradient(to bottom right, color-mix(in srgb, var(--color-primary-cta) 80%, transparent), var(--color-foreground));\nbox-shadow: 2.10837px 3.16256px 9.48767px color-mix(in srgb, var(--color-accent) 30%, transparent);",
"double-inset": "background: var(--color-primary-cta);\nbox-shadow: color-mix(in srgb, var(--color-background) 15%, transparent) 0px 4px 10px 0px inset, color-mix(in srgb, var(--color-background) 15%, transparent) 0px -4px 8px 0px inset;",
"primary-glow": "background: var(--color-primary-cta);\nbox-shadow: color-mix(in srgb, var(--color-background) 20%, transparent) 0px 3px 1px 0px inset, color-mix(in srgb, var(--color-primary-cta) 13%, transparent) 0px 0.839802px 0.503881px -0.3125px, color-mix(in srgb, var(--color-primary-cta) 13%, transparent) 0px 1.99048px 1.19429px -0.625px, color-mix(in srgb, var(--color-primary-cta) 13%, transparent) 0px 3.63084px 2.1785px -0.9375px, color-mix(in srgb, var(--color-primary-cta) 13%, transparent) 0px 6.03627px 3.62176px -1.25px, color-mix(in srgb, var(--color-primary-cta) 13%, transparent) 0px 9.74808px 5.84885px -1.5625px, color-mix(in srgb, var(--color-primary-cta) 13%, transparent) 0px 15.9566px 9.57398px -1.875px, color-mix(in srgb, var(--color-primary-cta) 13%, transparent) 0px 27.4762px 16.4857px -2.1875px, color-mix(in srgb, var(--color-primary-cta) 13%, transparent) 0px 50px 30px -2.5px;",
"inset-glow": "background: linear-gradient(180deg, color-mix(in srgb, var(--color-primary-cta) 65%, var(--color-background)) -35%, var(--color-primary-cta) 65%);\nbox-shadow: 0 10px 18px -7px color-mix(in srgb, var(--color-background) 50%, transparent), inset 0 1px 0 0 color-mix(in srgb, var(--color-foreground) 15%, transparent);\nborder: 1px solid color-mix(in srgb, var(--color-foreground) 20%, transparent);",
"soft-glow": "background: radial-gradient(ellipse at 50% -20%, color-mix(in srgb, var(--color-primary-cta) 70%, var(--color-foreground)) 0%, var(--color-primary-cta) 70%);\nbox-shadow: 0 8px 24px -6px color-mix(in srgb, var(--color-primary-cta) 35%, transparent), inset 0 1px 0 0 color-mix(in srgb, var(--color-foreground) 20%, transparent);",
"glass-shimmer": "background: linear-gradient(165deg, color-mix(in srgb, var(--color-primary-cta) 85%, var(--color-foreground)) 0%, var(--color-primary-cta) 40%, color-mix(in srgb, var(--color-primary-cta) 90%, var(--color-background)) 100%);\nbox-shadow: inset 0 1px 1px 0 color-mix(in srgb, var(--color-foreground) 25%, transparent), inset 0 -1px 1px 0 color-mix(in srgb, var(--color-background) 15%, transparent), 0 4px 12px -2px color-mix(in srgb, var(--color-primary-cta) 25%, transparent);",
"neon-outline": "background: var(--color-primary-cta);\nbox-shadow: 0 0 5px color-mix(in srgb, var(--color-accent) 50%, transparent), 0 0 15px color-mix(in srgb, var(--color-accent) 30%, transparent), 0 0 30px color-mix(in srgb, var(--color-accent) 15%, transparent), inset 0 0 8px color-mix(in srgb, var(--color-accent) 10%, transparent);",
"lifted": "background: linear-gradient(180deg, color-mix(in srgb, var(--color-primary-cta) 95%, var(--color-foreground)) 0%, var(--color-primary-cta) 50%, color-mix(in srgb, var(--color-primary-cta) 95%, var(--color-background)) 100%);\nbox-shadow: inset 0 2px 3px 0 color-mix(in srgb, var(--color-foreground) 20%, transparent), inset 0 -2px 3px 0 color-mix(in srgb, var(--color-background) 25%, transparent), 0 2px 4px -1px color-mix(in srgb, var(--color-background) 40%, transparent);",
"depth-layers": "background: var(--color-primary-cta);\nbox-shadow: 0 1px 2px color-mix(in srgb, var(--color-primary-cta) 20%, transparent), 0 2px 4px color-mix(in srgb, var(--color-primary-cta) 20%, transparent), 0 4px 8px color-mix(in srgb, var(--color-primary-cta) 15%, transparent), 0 8px 16px color-mix(in srgb, var(--color-primary-cta) 10%, transparent), 0 16px 32px color-mix(in srgb, var(--color-primary-cta) 5%, transparent);",
"accent-edge": "background: linear-gradient(180deg, var(--color-primary-cta) 0%, color-mix(in srgb, var(--color-primary-cta) 90%, var(--color-background)) 100%);\nbox-shadow: 0 0 0 1px color-mix(in srgb, var(--color-accent) 60%, transparent), 0 4px 12px -2px color-mix(in srgb, var(--color-accent) 35%, transparent), inset 0 1px 0 0 color-mix(in srgb, var(--color-foreground) 20%, transparent);",
"metallic": "background: linear-gradient(135deg, color-mix(in srgb, var(--color-primary-cta) 80%, var(--color-foreground)) 0%, var(--color-primary-cta) 25%, color-mix(in srgb, var(--color-primary-cta) 90%, var(--color-background)) 50%, var(--color-primary-cta) 75%, color-mix(in srgb, var(--color-primary-cta) 85%, var(--color-foreground)) 100%);\nbox-shadow: inset 0 1px 0 0 color-mix(in srgb, var(--color-foreground) 30%, transparent), 0 3px 8px -2px color-mix(in srgb, var(--color-background) 50%, transparent);"
},
"secondaryButtons": {
"glass": "backdrop-filter: blur(8px);\nbackground: linear-gradient(to bottom right, color-mix(in srgb, var(--color-secondary-cta) 80%, transparent), var(--color-secondary-cta));\nbox-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);\nborder: 1px solid var(--color-secondary-cta);",
"solid": "background: var(--color-secondary-cta);",
"layered": "background:\n linear-gradient(color-mix(in srgb, var(--color-accent) 5%, transparent) 0%, transparent 59.26%),\n linear-gradient(var(--color-secondary-cta), var(--color-secondary-cta)),\n linear-gradient(var(--color-secondary-cta), var(--color-secondary-cta)),\n linear-gradient(color-mix(in srgb, var(--color-accent) 5%, transparent) 0%, transparent 59.26%),\n linear-gradient(color-mix(in srgb, var(--color-secondary-cta) 60%, transparent), color-mix(in srgb, var(--color-secondary-cta) 60%, transparent)),\n var(--color-secondary-cta);\nbox-shadow:\n 2.10837px 3.16256px 9.48767px color-mix(in srgb, var(--color-accent) 10%, transparent);\nborder: 1px solid var(--color-secondary-cta);",
"radial-glow": "background:\n radial-gradient(circle at 0% 0%, color-mix(in srgb, var(--color-accent) 15%, transparent) 0%, transparent 40%),\n radial-gradient(circle at 100% 100%, color-mix(in srgb, var(--color-accent) 15%, transparent) 0%, transparent 40%),\n var(--color-secondary-cta);\nbox-shadow: 2.10837px 3.16256px 9.48767px color-mix(in srgb, var(--color-accent) 10%, transparent);"
}
}

23
eslint.config.js Normal file
View File

@@ -0,0 +1,23 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
},
])

237
fontThemes.json Normal file
View File

@@ -0,0 +1,237 @@
{
"singleFonts": {
"interTight": {
"name": "Inter Tight",
"import": "import { Inter_Tight } from \"next/font/google\";",
"initialization": "const interTight = Inter_Tight({\n variable: \"--font-inter-tight\",\n subsets: [\"latin\"],\n weight: [\"100\", \"200\", \"300\", \"400\", \"500\", \"600\", \"700\", \"800\", \"900\"],\n});",
"className": "${interTight.variable}",
"cssVariable": "font-family: var(--font-inter-tight), sans-serif;"
},
"inter": {
"name": "Inter",
"import": "import { Inter } from \"next/font/google\";",
"initialization": "const inter = Inter({\n variable: \"--font-inter\",\n subsets: [\"latin\"],\n});",
"className": "${inter.variable}",
"cssVariable": "font-family: var(--font-inter), sans-serif;"
},
"poppins": {
"name": "Poppins",
"import": "import { Poppins } from \"next/font/google\";",
"initialization": "const poppins = Poppins({\n variable: \"--font-poppins\",\n subsets: [\"latin\"],\n weight: [\"100\", \"200\", \"300\", \"400\", \"500\", \"600\", \"700\", \"800\", \"900\"],\n});",
"className": "${poppins.variable}",
"cssVariable": "font-family: var(--font-poppins), sans-serif;"
},
"montserrat": {
"name": "Montserrat",
"import": "import { Montserrat } from \"next/font/google\";",
"initialization": "const montserrat = Montserrat({\n variable: \"--font-montserrat\",\n subsets: [\"latin\"],\n});",
"className": "${montserrat.variable}",
"cssVariable": "font-family: var(--font-montserrat), sans-serif;"
},
"roboto": {
"name": "Roboto",
"import": "import { Roboto } from \"next/font/google\";",
"initialization": "const roboto = Roboto({\n variable: \"--font-roboto\",\n subsets: [\"latin\"],\n weight: [\"100\", \"300\", \"400\", \"500\", \"700\", \"900\"],\n});",
"className": "${roboto.variable}",
"cssVariable": "font-family: var(--font-roboto), sans-serif;"
},
"openSans": {
"name": "Open Sans",
"import": "import { Open_Sans } from \"next/font/google\";",
"initialization": "const openSans = Open_Sans({\n variable: \"--font-open-sans\",\n subsets: [\"latin\"],\n});",
"className": "${openSans.variable}",
"cssVariable": "font-family: var(--font-open-sans), sans-serif;"
},
"lato": {
"name": "Lato",
"import": "import { Lato } from \"next/font/google\";",
"initialization": "const lato = Lato({\n variable: \"--font-lato\",\n subsets: [\"latin\"],\n weight: [\"100\", \"300\", \"400\", \"700\", \"900\"],\n});",
"className": "${lato.variable}",
"cssVariable": "font-family: var(--font-lato), sans-serif;"
},
"dmSans": {
"name": "DM Sans",
"import": "import { DM_Sans } from \"next/font/google\";",
"initialization": "const dmSans = DM_Sans({\n variable: \"--font-dm-sans\",\n subsets: [\"latin\"],\n});",
"className": "${dmSans.variable}",
"cssVariable": "font-family: var(--font-dm-sans), sans-serif;"
},
"manrope": {
"name": "Manrope",
"import": "import { Manrope } from \"next/font/google\";",
"initialization": "const manrope = Manrope({\n variable: \"--font-manrope\",\n subsets: [\"latin\"],\n});",
"className": "${manrope.variable}",
"cssVariable": "font-family: var(--font-manrope), sans-serif;"
},
"sourceSans3": {
"name": "Source Sans 3",
"import": "import { Source_Sans_3 } from \"next/font/google\";",
"initialization": "const sourceSans3 = Source_Sans_3({\n variable: \"--font-source-sans-3\",\n subsets: [\"latin\"],\n});",
"className": "${sourceSans3.variable}",
"cssVariable": "font-family: var(--font-source-sans-3), sans-serif;"
},
"publicSans": {
"name": "Public Sans",
"import": "import { Public_Sans } from \"next/font/google\";",
"initialization": "const publicSans = Public_Sans({\n variable: \"--font-public-sans\",\n subsets: [\"latin\"],\n});",
"className": "${publicSans.variable}",
"cssVariable": "font-family: var(--font-public-sans), sans-serif;"
},
"mulish": {
"name": "Mulish",
"import": "import { Mulish } from \"next/font/google\";",
"initialization": "const mulish = Mulish({\n variable: \"--font-mulish\",\n subsets: [\"latin\"],\n});",
"className": "${mulish.variable}",
"cssVariable": "font-family: var(--font-mulish), sans-serif;"
},
"nunito": {
"name": "Nunito",
"import": "import { Nunito } from \"next/font/google\";",
"initialization": "const nunito = Nunito({\n variable: \"--font-nunito\",\n subsets: [\"latin\"],\n});",
"className": "${nunito.variable}",
"cssVariable": "font-family: var(--font-nunito), sans-serif;"
},
"nunitoSans": {
"name": "Nunito Sans",
"import": "import { Nunito_Sans } from \"next/font/google\";",
"initialization": "const nunitoSans = Nunito_Sans({\n variable: \"--font-nunito-sans\",\n subsets: [\"latin\"],\n});",
"className": "${nunitoSans.variable}",
"cssVariable": "font-family: var(--font-nunito-sans), sans-serif;"
},
"raleway": {
"name": "Raleway",
"import": "import { Raleway } from \"next/font/google\";",
"initialization": "const raleway = Raleway({\n variable: \"--font-raleway\",\n subsets: [\"latin\"],\n});",
"className": "${raleway.variable}",
"cssVariable": "font-family: var(--font-raleway), sans-serif;"
},
"archivo": {
"name": "Archivo",
"import": "import { Archivo } from \"next/font/google\";",
"initialization": "const archivo = Archivo({\n variable: \"--font-archivo\",\n subsets: [\"latin\"],\n});",
"className": "${archivo.variable}",
"cssVariable": "font-family: var(--font-archivo), sans-serif;"
},
"figtree": {
"name": "Figtree",
"import": "import { Figtree } from \"next/font/google\";",
"initialization": "const figtree = Figtree({\n variable: \"--font-figtree\",\n subsets: [\"latin\"],\n});",
"className": "${figtree.variable}",
"cssVariable": "font-family: var(--font-figtree), sans-serif;"
}
},
"fontPairings": {
"interOpenSans": {
"name": "Inter + Open Sans",
"description": "Neutral headings with friendly body. Clean and approachable.",
"headingFont": "inter",
"bodyFont": "openSans",
"imports": "import { Inter } from \"next/font/google\";\nimport { Open_Sans } from \"next/font/google\";",
"initializations": "const inter = Inter({\n variable: \"--font-inter\",\n subsets: [\"latin\"],\n});\n\nconst openSans = Open_Sans({\n variable: \"--font-open-sans\",\n subsets: [\"latin\"],\n});",
"classNames": "${inter.variable} ${openSans.variable}",
"globalsCss": {
"instructions": "Update the font-family property within the existing CSS rules in globals.css (@layer base section)",
"bodyRule": "body {\n /* ... existing properties ... */\n font-family: var(--font-open-sans), sans-serif;\n}",
"headingsRule": "h1, h2, h3, h4, h5, h6 {\n font-family: var(--font-inter), sans-serif;\n}",
"bodyFontFamily": "var(--font-open-sans), sans-serif",
"headingsFontFamily": "var(--font-inter), sans-serif"
}
},
"dmSansInter": {
"name": "DM Sans + Inter",
"description": "Modern geometric headings with neutral body. Contemporary and clean.",
"headingFont": "dmSans",
"bodyFont": "inter",
"imports": "import { DM_Sans } from \"next/font/google\";\nimport { Inter } from \"next/font/google\";",
"initializations": "const dmSans = DM_Sans({\n variable: \"--font-dm-sans\",\n subsets: [\"latin\"],\n});\n\nconst inter = Inter({\n variable: \"--font-inter\",\n subsets: [\"latin\"],\n});",
"classNames": "${dmSans.variable} ${inter.variable}",
"globalsCss": {
"instructions": "Update the font-family property within the existing CSS rules in globals.css (@layer base section)",
"bodyRule": "body {\n /* ... existing properties ... */\n font-family: var(--font-inter), sans-serif;\n}",
"headingsRule": "h1, h2, h3, h4, h5, h6 {\n font-family: var(--font-dm-sans), sans-serif;\n}",
"bodyFontFamily": "var(--font-inter), sans-serif",
"headingsFontFamily": "var(--font-dm-sans), sans-serif"
}
},
"manropeDmSans": {
"name": "Manrope + DM Sans",
"description": "Geometric headings with clean body. Modern and professional.",
"headingFont": "manrope",
"bodyFont": "dmSans",
"imports": "import { Manrope } from \"next/font/google\";\nimport { DM_Sans } from \"next/font/google\";",
"initializations": "const manrope = Manrope({\n variable: \"--font-manrope\",\n subsets: [\"latin\"],\n});\n\nconst dmSans = DM_Sans({\n variable: \"--font-dm-sans\",\n subsets: [\"latin\"],\n});",
"classNames": "${manrope.variable} ${dmSans.variable}",
"globalsCss": {
"instructions": "Update the font-family property within the existing CSS rules in globals.css (@layer base section)",
"bodyRule": "body {\n /* ... existing properties ... */\n font-family: var(--font-dm-sans), sans-serif;\n}",
"headingsRule": "h1, h2, h3, h4, h5, h6 {\n font-family: var(--font-manrope), sans-serif;\n}",
"bodyFontFamily": "var(--font-dm-sans), sans-serif",
"headingsFontFamily": "var(--font-manrope), sans-serif"
}
},
"publicSansInter": {
"name": "Public Sans + Inter",
"description": "Government-inspired headings with neutral body. Professional and trustworthy.",
"headingFont": "publicSans",
"bodyFont": "inter",
"imports": "import { Public_Sans } from \"next/font/google\";\nimport { Inter } from \"next/font/google\";",
"initializations": "const publicSans = Public_Sans({\n variable: \"--font-public-sans\",\n subsets: [\"latin\"],\n});\n\nconst inter = Inter({\n variable: \"--font-inter\",\n subsets: [\"latin\"],\n});",
"classNames": "${publicSans.variable} ${inter.variable}",
"globalsCss": {
"instructions": "Update the font-family property within the existing CSS rules in globals.css (@layer base section)",
"bodyRule": "body {\n /* ... existing properties ... */\n font-family: var(--font-inter), sans-serif;\n}",
"headingsRule": "h1, h2, h3, h4, h5, h6 {\n font-family: var(--font-public-sans), sans-serif;\n}",
"bodyFontFamily": "var(--font-inter), sans-serif",
"headingsFontFamily": "var(--font-public-sans), sans-serif"
}
},
"mulishInter": {
"name": "Mulish + Inter",
"description": "Minimal headings with neutral body. Clean and modern.",
"headingFont": "mulish",
"bodyFont": "inter",
"imports": "import { Mulish } from \"next/font/google\";\nimport { Inter } from \"next/font/google\";",
"initializations": "const mulish = Mulish({\n variable: \"--font-mulish\",\n subsets: [\"latin\"],\n});\n\nconst inter = Inter({\n variable: \"--font-inter\",\n subsets: [\"latin\"],\n});",
"classNames": "${mulish.variable} ${inter.variable}",
"globalsCss": {
"instructions": "Update the font-family property within the existing CSS rules in globals.css (@layer base section)",
"bodyRule": "body {\n /* ... existing properties ... */\n font-family: var(--font-inter), sans-serif;\n}",
"headingsRule": "h1, h2, h3, h4, h5, h6 {\n font-family: var(--font-mulish), sans-serif;\n}",
"bodyFontFamily": "var(--font-inter), sans-serif",
"headingsFontFamily": "var(--font-mulish), sans-serif"
}
},
"montserratInter": {
"name": "Montserrat + Inter",
"description": "Geometric sans-serif headings with neutral body. Popular and reliable.",
"headingFont": "montserrat",
"bodyFont": "inter",
"imports": "import { Montserrat } from \"next/font/google\";\nimport { Inter } from \"next/font/google\";",
"initializations": "const montserrat = Montserrat({\n variable: \"--font-montserrat\",\n subsets: [\"latin\"],\n});\n\nconst inter = Inter({\n variable: \"--font-inter\",\n subsets: [\"latin\"],\n});",
"classNames": "${montserrat.variable} ${inter.variable}",
"globalsCss": {
"instructions": "Update the font-family property within the existing CSS rules in globals.css (@layer base section)",
"bodyRule": "body {\n /* ... existing properties ... */\n font-family: var(--font-inter), sans-serif;\n}",
"headingsRule": "h1, h2, h3, h4, h5, h6 {\n font-family: var(--font-montserrat), sans-serif;\n}",
"bodyFontFamily": "var(--font-inter), sans-serif",
"headingsFontFamily": "var(--font-montserrat), sans-serif"
}
},
"libreBaskervilleInter": {
"name": "Libre Baskerville + Inter",
"description": "Classic serif headings with neutral body. Elegant and readable.",
"headingFont": "libreBaskerville",
"bodyFont": "inter",
"imports": "import { Libre_Baskerville } from \"next/font/google\";\nimport { Inter } from \"next/font/google\";",
"initializations": "const libreBaskerville = Libre_Baskerville({\n variable: \"--font-libre-baskerville\",\n subsets: [\"latin\"],\n weight: [\"400\", \"700\"],\n});\n\nconst inter = Inter({\n variable: \"--font-inter\",\n subsets: [\"latin\"],\n});",
"classNames": "${libreBaskerville.variable} ${inter.variable}",
"globalsCss": {
"instructions": "Update the font-family property within the existing CSS rules in globals.css (@layer base section)",
"bodyRule": "body {\n /* ... existing properties ... */\n font-family: var(--font-inter), sans-serif;\n}",
"headingsRule": "h1, h2, h3, h4, h5, h6 {\n font-family: var(--font-libre-baskerville), serif;\n}",
"bodyFontFamily": "var(--font-inter), sans-serif",
"headingsFontFamily": "var(--font-libre-baskerville), serif"
}
}
}
}

13
index.html Normal file
View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Webild Components</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

40
package.json Normal file
View File

@@ -0,0 +1,40 @@
{
"name": "webild-components",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview",
"typecheck": "tsc --noEmit --project tsconfig.app.json"
},
"dependencies": {
"@tailwindcss/vite": "^4.2.2",
"clsx": "^2.1.1",
"embla-carousel": "^8.6.0",
"embla-carousel-react": "^8.6.0",
"motion": "^12.38.0",
"lucide-react": "^1.7.0",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-router-dom": "^7.14.0",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4.2.2"
},
"devDependencies": {
"@eslint/js": "^9.39.4",
"@types/node": "^24.12.2",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1",
"eslint": "^9.39.4",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.5.2",
"globals": "^17.4.0",
"typescript": "~6.0.2",
"typescript-eslint": "^8.58.0",
"vite": "^8.0.4"
}
}

2228
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

1
public/favicon.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.3 KiB

1010
registry.json Normal file

File diff suppressed because it is too large Load Diff

280
src/App.tsx Normal file
View File

@@ -0,0 +1,280 @@
import AboutMediaOverlay from '@/components/sections/about/AboutMediaOverlay';
import FeaturesLabeledList from '@/components/sections/features/FeaturesLabeledList';
import FooterSimpleCard from '@/components/sections/footer/FooterSimpleCard';
import HeroBillboardGallery from '@/components/sections/hero/HeroBillboardGallery';
import MetricsGradientCards from '@/components/sections/metrics/MetricsGradientCards';
import NavbarCentered from '@/components/ui/NavbarCentered';
import TestimonialSplitCards from '@/components/sections/testimonial/TestimonialSplitCards';
import { Award, Shield, Star } from "lucide-react";
export default function App() {
return (
<>
<div id="nav" data-section="nav">
<NavbarCentered
logo="Kikwala"
navItems={[
{
name: "About",
id: "about",
},
{
name: "Services",
id: "features",
},
{
name: "Metrics",
id: "metrics",
},
]}
ctaButton={{
text: "Contact Us",
href: "#contact",
}}
/>
</div>
<div id="hero" data-section="hero">
<HeroBillboardGallery
tag="Innovative Solutions"
title="Redefining the Future of Kikwala Group"
description="We blend cutting-edge technology with strategic design to build seamless, future-ready experiences."
primaryButton={{
text: "Explore Services",
href: "#features",
}}
secondaryButton={{
text: "Learn More",
href: "#about",
}}
items={[
{
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3A2kdHi1NSExmmN97qC0PBBxl6G/professional-corporate-architecture-mode-1776091009314-91da2c66.png",
},
{
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3A2kdHi1NSExmmN97qC0PBBxl6G/abstract-geometric-shapes-in-soft-blue-a-1776091018661-488dce9e.png",
},
{
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3A2kdHi1NSExmmN97qC0PBBxl6G/close-up-of-a-modern-laptop-workspace-wi-1776091030671-cb60234b.png",
},
{
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3A2kdHi1NSExmmN97qC0PBBxl6G/minimalist-conference-room-glass-walls-s-1776091039888-bae26767.png",
},
{
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3A2kdHi1NSExmmN97qC0PBBxl6G/creative-design-studio-interior-light-wo-1776091048661-bb8c4a50.png",
},
{
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3A2kdHi1NSExmmN97qC0PBBxl6G/modern-business-cityscape-aerial-view-bl-1776091058873-a76defb2.png",
},
]}
/>
</div>
<div id="about" data-section="about">
<AboutMediaOverlay
tag="About Us"
title="Where Vision Meets Precision"
description="Kikwala Group is dedicated to delivering excellence across digital platforms. Our approach focuses on long-term sustainability and aesthetic innovation."
imageSrc="https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3A2kdHi1NSExmmN97qC0PBBxl6G/professional-team-working-in-a-high-tech-1776091068654-92bcd5ea.png"
/>
</div>
<div id="features" data-section="features">
<FeaturesLabeledList
tag="Core Services"
title="What We Deliver"
description="Comprehensive suite of digital services designed to scale."
items={[
{
label: "Strategy",
title: "Strategic Digital Consulting",
bullets: [
"Business modeling",
"Market entry analysis",
"Growth optimization",
],
primaryButton: {
text: "View Details",
href: "#",
},
secondaryButton: {
text: "Contact",
href: "#contact",
},
},
{
label: "Design",
title: "UI & UX Design",
bullets: [
"User research",
"Prototype design",
"Interactive experiences",
],
primaryButton: {
text: "View Details",
href: "#",
},
secondaryButton: {
text: "Contact",
href: "#contact",
},
},
{
label: "Tech",
title: "Advanced Engineering",
bullets: [
"Cloud architecture",
"Custom application development",
"Security protocols",
],
primaryButton: {
text: "View Details",
href: "#",
},
secondaryButton: {
text: "Contact",
href: "#contact",
},
},
]}
/>
</div>
<div id="metrics" data-section="metrics">
<MetricsGradientCards
tag="Our Impact"
title="Measurable Results"
description="Data-driven performance for every project we undertake."
metrics={[
{
value: "150+",
title: "Successful Projects",
description: "High-impact deployments delivered.",
icon: Award,
},
{
value: "98%",
title: "Client Satisfaction",
description: "Consistently meeting high standards.",
icon: Star,
},
{
value: "40+",
title: "Global Partners",
description: "Extensive network of innovators.",
icon: Shield,
},
]}
/>
</div>
<div id="testimonial" data-section="testimonial">
<TestimonialSplitCards
tag="Client Stories"
title="What Partners Say"
description="Trusted by leading global innovators."
testimonials={[
{
tag: "Innovation",
title: "Game-changing solution",
quote: "Kikwala Group transformed our digital strategy with incredible precision.",
name: "Sarah Chen",
date: "2023-09-12",
avatarImageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3A2kdHi1NSExmmN97qC0PBBxl6G/headshot-of-professional-business-person-1776091077117-de145c03.png",
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3A2kdHi1NSExmmN97qC0PBBxl6G/abstract-modern-office-architecture-glas-1776091127265-b2d4a12b.png",
},
{
tag: "Growth",
title: "Unrivaled expertise",
quote: "The team provided deep insights that helped us scale our platform efficiently.",
name: "Michael Ross",
date: "2023-11-05",
avatarImageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3A2kdHi1NSExmmN97qC0PBBxl6G/headshot-of-professional-woman-business--1776091088512-80201065.png",
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3A2kdHi1NSExmmN97qC0PBBxl6G/minimalist-design-texture-clean-white-an-1776091135466-6d81bda8.png",
},
{
tag: "Strategy",
title: "Outstanding results",
quote: "Strategic thinkers who deliver results consistently and professionally.",
name: "Elena Rodriguez",
date: "2024-01-20",
avatarImageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3A2kdHi1NSExmmN97qC0PBBxl6G/headshot-of-tech-business-person-clean-a-1776091098842-4ea10ba3.png",
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3A2kdHi1NSExmmN97qC0PBBxl6G/modern-office-building-glass-exterior-br-1776091144537-6703e6f3.png",
},
{
tag: "Design",
title: "Visionary design",
quote: "Their design process is fluid, intuitive, and truly visionary.",
name: "David Kim",
date: "2024-03-14",
avatarImageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3A2kdHi1NSExmmN97qC0PBBxl6G/headshot-of-professional-business-execut-1776091109322-2044fb88.png",
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3A2kdHi1NSExmmN97qC0PBBxl6G/professional-office-setup-laptop-and-dig-1776091153984-849c58a1.png",
},
{
tag: "Engineering",
title: "Reliable partner",
quote: "A tech partner that treats your project as their own.",
name: "Alex Johnson",
date: "2024-05-10",
avatarImageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3A2kdHi1NSExmmN97qC0PBBxl6G/headshot-of-professional-business-entrep-1776091117968-b4c0e85c.png",
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3A2kdHi1NSExmmN97qC0PBBxl6G/abstract-business-meeting-soft-focus-bac-1776091163608-3b4d564d.png",
},
]}
/>
</div>
<div id="footer" data-section="footer">
<FooterSimpleCard
brand="Kikwala Group"
columns={[
{
title: "Company",
items: [
{
label: "About Us",
href: "#about",
},
{
label: "Careers",
href: "#",
},
{
label: "Press",
href: "#",
},
],
},
{
title: "Services",
items: [
{
label: "Strategy",
href: "#",
},
{
label: "Design",
href: "#",
},
{
label: "Engineering",
href: "#",
},
],
},
]}
copyright="© 2024 Kikwala Group. All rights reserved."
links={[
{
label: "Privacy Policy",
href: "#",
},
{
label: "Terms of Service",
href: "#",
},
]}
/>
</div>
</>
);
}

View File

@@ -0,0 +1,70 @@
import { motion } from "motion/react";
import Button from "@/components/ui/Button";
import TextAnimation from "@/components/ui/TextAnimation";
import ImageOrVideo from "@/components/ui/ImageOrVideo";
type AboutMediaOverlayProps = {
tag: string;
title: string;
description: string;
primaryButton?: { text: string; href: string };
secondaryButton?: { text: string; href: string };
} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never });
const AboutMediaOverlay = ({
tag,
title,
description,
primaryButton,
secondaryButton,
imageSrc,
videoSrc,
}: AboutMediaOverlayProps) => {
return (
<section aria-label="About section" className="py-20">
<div className="relative flex items-center justify-center py-8 md:py-12 mx-auto w-content-width rounded overflow-hidden">
<div className="absolute inset-0">
<ImageOrVideo imageSrc={imageSrc} videoSrc={videoSrc} />
<div className="absolute inset-0 bg-background/40 backdrop-blur-xs pointer-events-none select-none" />
</div>
<div className="relative z-10 flex items-center justify-center px-5 py-10 mx-auto min-h-100 md:min-h-120 md:w-1/2 w-content-width">
<div className="flex flex-col items-center gap-3 md:gap-1 text-center">
<motion.span
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-15%" }}
transition={{ duration: 0.4, ease: "easeOut" }}
className="mb-3 px-3 py-1 text-sm card rounded"
>
{tag}
</motion.span>
<TextAnimation
text={title}
variant="slide-up"
tag="h2"
className="text-6xl font-medium text-balance"
/>
<TextAnimation
text={description}
variant="slide-up"
tag="p"
className="text-base md:text-lg leading-tight"
/>
{(primaryButton || secondaryButton) && (
<div className="flex flex-wrap max-md:justify-center gap-3 mt-3">
{primaryButton && <Button text={primaryButton.text} href={primaryButton.href} variant="primary" animate />}
{secondaryButton && <Button text={secondaryButton.text} href={secondaryButton.href} variant="secondary" animate delay={0.1} />}
</div>
)}
</div>
</div>
</div>
</section>
);
};
export default AboutMediaOverlay;

View File

@@ -0,0 +1,100 @@
import { Fragment } from "react";
import { motion } from "motion/react";
import Button from "@/components/ui/Button";
import TextAnimation from "@/components/ui/TextAnimation";
type FeatureItem = {
label: string;
title: string;
bullets: string[];
primaryButton: { text: string; href: string };
secondaryButton: { text: string; href: string };
};
interface FeaturesLabeledListProps {
tag: string;
title: string;
description: string;
primaryButton?: { text: string; href: string };
secondaryButton?: { text: string; href: string };
items: FeatureItem[];
}
const FeaturesLabeledList = ({
tag,
title,
description,
primaryButton,
secondaryButton,
items,
}: FeaturesLabeledListProps) => {
return (
<section aria-label="Features section" className="py-20">
<div className="flex flex-col gap-8">
<div className="flex flex-col items-center w-content-width mx-auto gap-3 md:gap-2">
<span className="px-3 py-1 text-sm card rounded">{tag}</span>
<TextAnimation
text={title}
variant="slide-up"
tag="h2"
className="text-6xl font-medium text-center text-balance"
/>
<TextAnimation
text={description}
variant="slide-up"
tag="p"
className="md:max-w-6/10 text-lg leading-tight text-center"
/>
{(primaryButton || secondaryButton) && (
<div className="flex flex-wrap justify-center gap-3 mt-1 md:mt-2">
{primaryButton && <Button text={primaryButton.text} href={primaryButton.href} variant="primary" animate />}
{secondaryButton && <Button text={secondaryButton.text} href={secondaryButton.href} variant="secondary" animate delay={0.1} />}
</div>
)}
</div>
<div className="flex flex-col gap-5 w-content-width mx-auto">
{items.map((item) => (
<motion.div
key={item.label}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-15%" }}
transition={{ duration: 0.6, ease: "easeOut" }}
className="flex flex-col md:flex-row gap-5 md:gap-12 p-5 md:p-12 card rounded"
>
<div className="w-full md:w-1/2 flex md:justify-start">
<h3 className="text-7xl font-medium leading-tight truncate">{item.label}</h3>
</div>
<div className="w-full h-px bg-foreground/20 md:hidden" />
<div className="flex flex-col w-full md:w-1/2 gap-5">
<h4 className="text-2xl md:text-3xl font-medium leading-tight">{item.title}</h4>
<div className="flex flex-wrap items-center gap-1">
{item.bullets.map((text, index) => (
<Fragment key={index}>
<span className="text-base">{text}</span>
{index < item.bullets.length - 1 && <span className="text-base text-accent"></span>}
</Fragment>
))}
</div>
<div className="flex flex-wrap gap-3 mt-2">
<Button text={item.primaryButton.text} href={item.primaryButton.href} variant="primary" />
<Button text={item.secondaryButton.text} href={item.secondaryButton.href} variant="secondary" />
</div>
</div>
</motion.div>
))}
</div>
</div>
</section>
);
};
export default FeaturesLabeledList;

View File

@@ -0,0 +1,82 @@
import { useButtonClick } from "@/hooks/useButtonClick";
type FooterLink = {
label: string;
href?: string;
onClick?: () => void;
};
type FooterColumn = {
title: string;
items: FooterLink[];
};
const FooterLinkItem = ({ label, href, onClick }: FooterLink) => {
const handleClick = useButtonClick(href, onClick);
return (
<button
onClick={handleClick}
className="text-base hover:opacity-75 transition-opacity cursor-pointer"
>
{label}
</button>
);
};
const FooterBottomLink = ({ label, href, onClick }: FooterLink) => {
const handleClick = useButtonClick(href, onClick);
return (
<button
onClick={handleClick}
className="text-sm opacity-50 hover:opacity-75 transition-opacity cursor-pointer"
>
{label}
</button>
);
};
const FooterSimpleCard = ({
brand,
columns,
copyright,
links,
}: {
brand: string;
columns: FooterColumn[];
copyright: string;
links: FooterLink[];
}) => {
return (
<footer aria-label="Site footer" className="w-full py-20">
<div className="w-content-width mx-auto p-10 rounded-lg card">
<div className="flex flex-col md:flex-row gap-10 md:gap-0 justify-between items-start mb-10">
<h2 className="text-4xl font-medium">{brand}</h2>
<div className="w-full md:w-fit flex flex-wrap gap-y-10 md:gap-12">
{columns.map((column) => (
<div key={column.title} className="w-1/2 md:w-auto flex flex-col items-start gap-3">
<h3 className="text-sm opacity-50">{column.title}</h3>
{column.items.map((item) => (
<FooterLinkItem key={item.label} label={item.label} href={item.href} onClick={item.onClick} />
))}
</div>
))}
</div>
</div>
<div className="flex items-center justify-between pt-8 border-t border-foreground/20">
<span className="text-sm opacity-50">{copyright}</span>
<div className="flex items-center gap-3">
{links.map((link) => (
<FooterBottomLink key={link.label} label={link.label} href={link.href} onClick={link.onClick} />
))}
</div>
</div>
</div>
</footer>
);
};
export default FooterSimpleCard;

View File

@@ -0,0 +1,99 @@
import { motion } from "motion/react";
import Button from "@/components/ui/Button";
import TextAnimation from "@/components/ui/TextAnimation";
import ImageOrVideo from "@/components/ui/ImageOrVideo";
import { cls } from "@/lib/utils";
interface HeroBillboardGalleryProps {
tag: string;
title: string;
description: string;
primaryButton: { text: string; href: string };
secondaryButton: { text: string; href: string };
items: ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never })[];
}
const HeroBillboardGallery = ({
tag,
title,
description,
primaryButton,
secondaryButton,
items,
}: HeroBillboardGalleryProps) => {
const marqueeItems = [...items, ...items];
const galleryStyles = [
"-rotate-6 z-10 -translate-y-5",
"rotate-6 z-20 translate-y-5 -ml-15",
"-rotate-6 z-30 -translate-y-5 -ml-15",
"rotate-6 z-40 translate-y-5 -ml-15",
"-rotate-6 z-50 -translate-y-5 -ml-15",
];
return (
<section aria-label="Hero section" className="flex items-center h-fit md:h-svh pt-25 pb-20 md:py-0">
<div className="flex flex-col items-center gap-10 md:gap-15 w-full md:w-content-width mx-auto">
<div className="flex flex-col items-center gap-2 w-content-width mx-auto text-center">
<span className="px-3 py-1 mb-1 text-sm card rounded">{tag}</span>
<TextAnimation
text={title}
variant="slide-up"
tag="h1"
className="text-6xl font-medium text-balance"
/>
<TextAnimation
text={description}
variant="slide-up"
tag="p"
className="text-base md:text-lg leading-tight text-balance"
/>
<div className="flex flex-wrap justify-center gap-3 mt-2">
<Button text={primaryButton.text} href={primaryButton.href} variant="primary" animate />
<Button text={secondaryButton.text} href={secondaryButton.href} variant="secondary" animate delay={0.1} />
</div>
</div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, ease: "easeOut", delay: 0.2 }}
className="block md:hidden w-full overflow-hidden mask-padding-x"
>
<div className="flex w-max animate-marquee-horizontal">
{marqueeItems.map((item, index) => (
<div key={index} className="shrink-0 w-[50vw] mr-5 aspect-4/5 p-2 card rounded overflow-hidden">
<ImageOrVideo imageSrc={item.imageSrc} videoSrc={item.videoSrc} />
</div>
))}
</div>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, ease: "easeOut", delay: 0.2 }}
className="hidden md:flex justify-center items-center w-full"
>
<div className="flex items-center justify-center">
{items.map((item, index) => (
<div
key={index}
className={cls(
"relative w-[23%] aspect-4/5 p-2 card rounded overflow-hidden shadow-lg transition-transform duration-500 ease-out hover:scale-110",
galleryStyles[index]
)}
>
<ImageOrVideo imageSrc={item.imageSrc} videoSrc={item.videoSrc} />
</div>
))}
</div>
</motion.div>
</div>
</section>
);
};
export default HeroBillboardGallery;

View File

@@ -0,0 +1,92 @@
import { motion } from "motion/react";
import type { LucideIcon } from "lucide-react";
import Button from "@/components/ui/Button";
import TextAnimation from "@/components/ui/TextAnimation";
import GridOrCarousel from "@/components/ui/GridOrCarousel";
type Metric = {
value: string;
title: string;
description: string;
icon: LucideIcon;
};
const MetricsGradientCards = ({
tag,
title,
description,
primaryButton,
secondaryButton,
metrics,
}: {
tag: string;
title: string;
description: string;
primaryButton?: { text: string; href: string };
secondaryButton?: { text: string; href: string };
metrics: Metric[];
}) => (
<section aria-label="Metrics section" className="py-20">
<div className="flex flex-col gap-8">
<div className="flex flex-col items-center gap-3 md:gap-2 w-content-width mx-auto">
<span className="px-3 py-1 text-sm card rounded">{tag}</span>
<TextAnimation
text={title}
variant="slide-up"
tag="h2"
className="text-6xl font-medium text-center text-balance"
/>
<TextAnimation
text={description}
variant="slide-up"
tag="p"
className="md:max-w-6/10 text-lg leading-tight text-center"
/>
{(primaryButton || secondaryButton) && (
<div className="flex flex-wrap justify-center gap-3 mt-1 md:mt-2">
{primaryButton && <Button text={primaryButton.text} href={primaryButton.href} variant="primary" animate />}
{secondaryButton && <Button text={secondaryButton.text} href={secondaryButton.href} variant="secondary" animate delay={0.1} />}
</div>
)}
</div>
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-15%" }}
transition={{ duration: 0.6, ease: "easeOut" }}
>
<GridOrCarousel carouselThreshold={3}>
{metrics.map((metric) => {
const IconComponent = metric.icon;
return (
<div key={metric.value} className="relative flex flex-col items-center justify-center gap-0 p-5 min-h-70 h-full card rounded">
<span
className="text-9xl font-medium leading-none text-center truncate"
style={{
backgroundImage: "linear-gradient(to bottom, var(--color-foreground) 0%, var(--color-foreground) 20%, transparent 72%)",
WebkitBackgroundClip: "text",
backgroundClip: "text",
WebkitTextFillColor: "transparent",
}}
>
{metric.value}
</span>
<span className="mt-[-0.75em] text-4xl font-medium text-center truncate">{metric.title}</span>
<p className="max-w-9/10 md:max-w-7/10 mt-2 text-base leading-tight text-center line-clamp-2">{metric.description}</p>
<div className="absolute bottom-5 left-5 flex items-center justify-center size-10 primary-button rounded">
<IconComponent className="h-2/5 w-2/5 text-primary-cta-text" />
</div>
</div>
);
})}
</GridOrCarousel>
</motion.div>
</div>
</section>
);
export default MetricsGradientCards;

View File

@@ -0,0 +1,137 @@
import { useCallback, useEffect, useState } from "react";
import { motion } from "motion/react";
import useEmblaCarousel from "embla-carousel-react";
import type { EmblaCarouselType } from "embla-carousel";
import Button from "@/components/ui/Button";
import TextAnimation from "@/components/ui/TextAnimation";
import ImageOrVideo from "@/components/ui/ImageOrVideo";
import { cls } from "@/lib/utils";
type Testimonial = {
tag: string;
title: string;
quote: string;
name: string;
date: string;
} & ({ avatarImageSrc: string; avatarVideoSrc?: never } | { avatarVideoSrc: string; avatarImageSrc?: never })
& ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never });
const TestimonialSplitCards = ({
tag,
title,
description,
primaryButton,
secondaryButton,
testimonials,
}: {
tag: string;
title: string;
description: string;
primaryButton?: { text: string; href: string };
secondaryButton?: { text: string; href: string };
testimonials: Testimonial[];
}) => {
const [emblaRef, emblaApi] = useEmblaCarousel({ loop: true, align: "center" });
const [selectedIndex, setSelectedIndex] = useState(0);
const scrollTo = useCallback((index: number) => emblaApi?.scrollTo(index), [emblaApi]);
const onSelect = useCallback((api: EmblaCarouselType) => {
setSelectedIndex(api.selectedScrollSnap());
}, []);
useEffect(() => {
if (!emblaApi) return;
onSelect(emblaApi);
emblaApi.on("select", onSelect).on("reInit", onSelect);
return () => {
emblaApi.off("select", onSelect).off("reInit", onSelect);
};
}, [emblaApi, onSelect]);
return (
<section aria-label="Testimonials section" className="py-20">
<div className="flex flex-col gap-8">
<div className="flex flex-col items-center gap-3 md:gap-2 w-content-width mx-auto">
<span className="px-3 py-1 text-sm card rounded">{tag}</span>
<TextAnimation
text={title}
variant="slide-up"
tag="h2"
className="text-6xl font-medium text-center text-balance"
/>
<TextAnimation
text={description}
variant="slide-up"
tag="p"
className="md:max-w-6/10 text-lg leading-tight text-center"
/>
{(primaryButton || secondaryButton) && (
<div className="flex flex-wrap justify-center gap-3 mt-1 md:mt-2">
{primaryButton && <Button text={primaryButton.text} href={primaryButton.href} variant="primary" animate />}
{secondaryButton && <Button text={secondaryButton.text} href={secondaryButton.href} variant="secondary" animate delay={0.1} />}
</div>
)}
</div>
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-15%" }}
transition={{ duration: 0.6, ease: "easeOut" }}
className="flex flex-col gap-5"
>
<div ref={emblaRef} className="overflow-hidden">
<div className="flex">
{testimonials.map((testimonial) => (
<div key={testimonial.name} className="flex-none w-content-width md:w-[calc(var(--width-content-width)*0.8)] mr-5">
<div className="flex flex-col justify-between md:grid md:grid-cols-2 h-full card rounded overflow-hidden">
<div className="flex flex-col justify-between gap-5 md:gap-8 p-5 md:p-10">
<div className="flex flex-col gap-3 md:gap-5">
<span className="px-3 py-1 w-fit text-sm card rounded">{testimonial.tag}</span>
<h3 className="text-3xl md:text-4xl font-medium leading-tight">{testimonial.title}</h3>
<p className="text-base md:text-lg leading-tight opacity-75">{testimonial.quote}</p>
</div>
<div className="flex items-center gap-3">
<div className="size-12 overflow-hidden rounded-full">
<ImageOrVideo imageSrc={testimonial.avatarImageSrc} videoSrc={testimonial.avatarVideoSrc} />
</div>
<div className="flex flex-col">
<span className="text-base font-medium leading-tight">{testimonial.name}</span>
<span className="text-sm leading-tight opacity-75">{testimonial.date}</span>
</div>
</div>
</div>
<div className="relative min-h-80 h-full md:aspect-square">
<ImageOrVideo imageSrc={testimonial.imageSrc} videoSrc={testimonial.videoSrc} />
</div>
</div>
</div>
))}
</div>
</div>
<div className="flex justify-center gap-2">
{testimonials.map((_, index) => (
<button
key={index}
onClick={() => scrollTo(index)}
className={cls("size-2 rounded-full transition-colors", index === selectedIndex ? "bg-foreground" : "bg-foreground/10")}
aria-label="Go to slide"
/>
))}
</div>
</motion.div>
</div>
</section>
);
};
export default TestimonialSplitCards;

View File

@@ -0,0 +1,46 @@
import { useState, useEffect } from "react";
import { cls } from "@/lib/utils";
const BARS = [
{ height: 100, hoverHeight: 40 },
{ height: 84, hoverHeight: 100 },
{ height: 62, hoverHeight: 75 },
{ height: 90, hoverHeight: 50 },
{ height: 70, hoverHeight: 90 },
{ height: 50, hoverHeight: 60 },
{ height: 75, hoverHeight: 85 },
{ height: 80, hoverHeight: 70 },
];
const AnimatedBarChart = () => {
const [active, setActive] = useState(2);
const [isHovered, setIsHovered] = useState(false);
useEffect(() => {
const interval = setInterval(() => setActive((p) => (p + 1) % BARS.length), 3000);
return () => clearInterval(interval);
}, []);
return (
<div
className="hidden md:block h-full w-full"
style={{ maskImage: "linear-gradient(to bottom, black 40%, transparent)" }}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<div className="flex items-end gap-4 h-full w-full">
{BARS.map((bar, i) => (
<div
key={i}
className="relative w-full rounded bg-background-accent transition-all duration-500"
style={{ height: `${isHovered ? bar.hoverHeight : bar.height}%` }}
>
<div className={cls("absolute inset-0 rounded primary-button transition-opacity duration-500", active === i ? "opacity-100" : "opacity-0")} />
</div>
))}
</div>
</div>
);
};
export default AnimatedBarChart;

View File

@@ -0,0 +1,67 @@
import { useRef, useEffect, useState } from "react";
import { cls } from "@/lib/utils";
const AutoFillText = ({
children,
className = "",
paddingY = "py-10",
}: {
children: string;
className?: string;
paddingY?: string;
}) => {
const containerRef = useRef<HTMLDivElement>(null);
const textRef = useRef<HTMLHeadingElement>(null);
const [fontSize, setFontSize] = useState<number | null>(null);
const hasDescenders = /[gjpqy]/.test(children);
const lineHeight = hasDescenders ? 1.2 : 0.8;
useEffect(() => {
const container = containerRef.current;
const text = textRef.current;
if (!container || !text) return;
const calculateSize = () => {
const containerWidth = container.offsetWidth;
if (containerWidth === 0) return;
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (!ctx) return;
const styles = getComputedStyle(text);
ctx.font = `${styles.fontWeight} 100px ${styles.fontFamily}`;
const textWidth = ctx.measureText(children).width;
if (textWidth > 0) {
setFontSize((containerWidth / textWidth) * 100);
}
};
calculateSize();
const observer = new ResizeObserver(calculateSize);
observer.observe(container);
return () => observer.disconnect();
}, [children]);
return (
<div ref={containerRef} className={cls("w-full min-w-0 flex-1", !hasDescenders && paddingY)}>
<h2
ref={textRef}
className={cls(
"whitespace-nowrap transition-opacity duration-150",
fontSize ? "opacity-100" : "opacity-0",
className
)}
style={{ fontSize: fontSize ? `${fontSize}px` : undefined, lineHeight }}
>
{children}
</h2>
</div>
);
};
export default AutoFillText;

View File

@@ -0,0 +1,44 @@
"use client";
import { motion } from "motion/react";
import { cls } from "@/lib/utils";
import { useButtonClick } from "@/hooks/useButtonClick";
interface ButtonProps {
text: string;
variant?: "primary" | "secondary";
href?: string;
onClick?: () => void;
animate?: boolean;
delay?: number;
className?: string;
}
const Button = ({ text, variant = "primary", href, onClick, animate = false, delay = 0, className = "" }: ButtonProps) => {
const handleClick = useButtonClick(href, onClick);
const classes = cls(
"flex items-center justify-center h-9 px-6 text-sm rounded cursor-pointer",
variant === "primary" ? "primary-button text-primary-cta-text" : "secondary-button text-secondary-cta-text",
className
);
const button = href
? <a href={href} onClick={handleClick} className={classes}>{text}</a>
: <button onClick={handleClick} className={classes}>{text}</button>;
if (!animate) return button;
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-15%" }}
transition={{ duration: 0.6, delay, ease: "easeOut" }}
>
{button}
</motion.div>
);
};
export default Button;

View File

@@ -0,0 +1,43 @@
import type { LucideIcon } from "lucide-react";
import { Send } from "lucide-react";
import { cls } from "@/lib/utils";
type Exchange = { userMessage: string; aiResponse: string };
const ChatMarquee = ({ aiIcon: AiIcon, userIcon: UserIcon, exchanges, placeholder }: { aiIcon: LucideIcon; userIcon: LucideIcon; exchanges: Exchange[]; placeholder: string }) => {
const messages = exchanges.flatMap((e) => [{ content: e.userMessage, isUser: true }, { content: e.aiResponse, isUser: false }]);
const duplicated = [...messages, ...messages];
return (
<div className="relative flex flex-col h-full w-full overflow-hidden">
<div className="flex-1 overflow-hidden mask-fade-y">
<div className="flex flex-col px-4 animate-marquee-vertical">
{duplicated.map((msg, i) => (
<div key={i} className={cls("flex items-end gap-2 mb-4 shrink-0", msg.isUser ? "flex-row-reverse" : "flex-row")}>
{msg.isUser ? (
<div className="flex items-center justify-center h-8 w-8 primary-button rounded shrink-0">
<UserIcon className="h-3 w-3 text-primary-cta-text" />
</div>
) : (
<div className="flex items-center justify-center h-8 w-8 card rounded shrink-0">
<AiIcon className="h-3 w-3" />
</div>
)}
<div className={cls("max-w-3/4 px-4 py-3 text-sm leading-tight", msg.isUser ? "primary-button rounded-2xl rounded-br-none text-primary-cta-text" : "card rounded-2xl rounded-bl-none")}>
{msg.content}
</div>
</div>
))}
</div>
</div>
<div className="flex items-center gap-2 p-2 pl-4 card rounded">
<p className="flex-1 text-sm text-foreground/75 truncate">{placeholder}</p>
<div className="flex items-center justify-center h-7 w-7 primary-button rounded">
<Send className="h-3 w-3 text-primary-cta-text" strokeWidth={1.75} />
</div>
</div>
</div>
);
};
export default ChatMarquee;

View File

@@ -0,0 +1,47 @@
import { Check, Loader } from "lucide-react";
import { cls } from "@/lib/utils";
type Item = { label: string; detail: string };
const DELAYS = [
["delay-150", "delay-200", "delay-[250ms]"],
["delay-[350ms]", "delay-[400ms]", "delay-[450ms]"],
["delay-[550ms]", "delay-[600ms]", "delay-[650ms]"],
];
const ChecklistTimeline = ({ heading, subheading, items, completedLabel }: { heading: string; subheading: string; items: [Item, Item, Item]; completedLabel: string }) => (
<div className="group relative flex items-center justify-center h-full w-full overflow-hidden">
<div className="absolute inset-0 flex items-center justify-center">
{[1, 0.8, 0.6].map((s) => <div key={s} className="absolute h-full aspect-square rounded-full border border-background-accent/30" style={{ transform: `scale(${s})` }} />)}
</div>
<div className="relative flex flex-col gap-3 p-4 max-w-full w-8/10 mask-fade-y">
<div className="flex items-center gap-2 p-3 card shadow rounded">
<Loader className="h-4 w-4 text-primary transition-transform duration-1000 group-hover:rotate-360" strokeWidth={1.5} />
<p className="text-xs truncate">{heading}</p>
<p className="text-xs text-foreground/75 ml-auto whitespace-nowrap">{subheading}</p>
</div>
{items.map((item, i) => (
<div key={i} className="flex items-center gap-2 px-3 py-2 card shadow rounded">
<div className="relative flex items-center justify-center h-6 w-6 card shadow rounded">
<div className="absolute h-2 w-2 primary-button rounded transition-opacity duration-300 group-hover:opacity-0" />
<div className={cls("absolute inset-0 flex items-center justify-center primary-button rounded opacity-0 scale-75 transition-all duration-300 group-hover:opacity-100 group-hover:scale-100", DELAYS[i][0])}>
<Check className="h-3 w-3 text-primary-cta-text" strokeWidth={2} />
</div>
</div>
<div className="flex-1 flex items-center justify-between gap-4 min-w-0">
<p className={cls("text-xs truncate opacity-0 transition-opacity duration-300 group-hover:opacity-100", DELAYS[i][1])}>{item.label}</p>
<p className={cls("text-xs text-foreground/75 whitespace-nowrap opacity-0 translate-y-1 transition-all duration-300 group-hover:opacity-100 group-hover:translate-y-0", DELAYS[i][2])}>{item.detail}</p>
</div>
</div>
))}
<div className="relative flex items-center justify-center p-3 primary-button rounded">
<div className="absolute flex gap-2 transition-opacity duration-500 delay-900 group-hover:opacity-0">
{[0, 1, 2].map((j) => <div key={j} className="h-2 w-2 rounded bg-primary-cta-text" />)}
</div>
<p className="text-xs text-primary-cta-text truncate opacity-0 transition-opacity duration-500 delay-900 group-hover:opacity-100">{completedLabel}</p>
</div>
</div>
</div>
);
export default ChecklistTimeline;

View File

@@ -0,0 +1,64 @@
import { Children, type ReactNode } from "react";
import useEmblaCarousel from "embla-carousel-react";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { cls } from "@/lib/utils";
import { useCarouselControls } from "@/hooks/useCarouselControls";
interface GridOrCarouselProps {
children: ReactNode;
carouselThreshold?: 2 | 3 | 4;
}
const GridOrCarousel = ({ children, carouselThreshold = 4 }: GridOrCarouselProps) => {
const [emblaRef, emblaApi] = useEmblaCarousel({ dragFree: true });
const { prevDisabled, nextDisabled, scrollPrev, scrollNext, scrollProgress } = useCarouselControls(emblaApi);
const items = Children.toArray(children);
const count = items.length;
if (count <= carouselThreshold) {
return (
<div className={cls(
"grid grid-cols-1 gap-5 mx-auto w-content-width",
count === 2 && "md:grid-cols-2",
count === 3 && "md:grid-cols-3",
count === 4 && "md:grid-cols-4"
)}>
{children}
</div>
);
}
return (
<div className="flex flex-col gap-5 w-full">
<div ref={emblaRef} className="overflow-hidden w-full cursor-grab">
<div className="flex gap-4">
<div className="shrink-0 w-carousel-padding" />
{items.map((child, i) => (
<div key={i} className={cls("shrink-0", carouselThreshold === 2 ? "w-carousel-item-2" : carouselThreshold === 3 ? "w-carousel-item-3" : "w-carousel-item-4")}>{child}</div>
))}
<div className="shrink-0 w-carousel-padding" />
</div>
</div>
<div className="flex w-full">
<div className="shrink-0 w-carousel-padding-controls" />
<div className="flex justify-between items-center w-full">
<div className="relative h-2 w-1/2 card rounded overflow-hidden">
<div className="absolute top-0 bottom-0 -left-full w-full primary-button rounded" style={{ transform: `translate3d(${scrollProgress}%,0px,0px)` }} />
</div>
<div className="flex items-center gap-3">
<button onClick={scrollPrev} disabled={prevDisabled} type="button" aria-label="Previous" className="flex items-center justify-center h-8 aspect-square secondary-button rounded cursor-pointer disabled:opacity-50">
<ChevronLeft className="h-2/5 aspect-square text-secondary-cta-text" />
</button>
<button onClick={scrollNext} disabled={nextDisabled} type="button" aria-label="Next" className="flex items-center justify-center h-8 aspect-square secondary-button rounded cursor-pointer disabled:opacity-50">
<ChevronRight className="h-2/5 aspect-square text-secondary-cta-text" />
</button>
</div>
</div>
<div className="shrink-0 w-carousel-padding-controls" />
</div>
</div>
);
};
export default GridOrCarousel;

View File

@@ -0,0 +1,61 @@
import { useRef, useState, useEffect } from "react";
import { motion, useMotionValue, useMotionTemplate } from "motion/react";
import { cls } from "@/lib/utils";
const CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
const randomChars = () => Array.from({ length: 1500 }, () => CHARS[Math.floor(Math.random() * 62)]).join("");
interface HoverPatternProps {
children: React.ReactNode;
className?: string;
}
const HoverPattern = ({ children, className = "" }: HoverPatternProps) => {
const ref = useRef<HTMLDivElement>(null);
const x = useMotionValue(0);
const y = useMotionValue(0);
const [chars, setChars] = useState(randomChars);
const [isMobile, setIsMobile] = useState(false);
useEffect(() => {
const checkMobile = () => setIsMobile(window.innerWidth < 768);
checkMobile();
window.addEventListener("resize", checkMobile);
return () => window.removeEventListener("resize", checkMobile);
}, []);
useEffect(() => {
if (isMobile && ref.current) {
x.set(ref.current.offsetWidth / 2);
y.set(ref.current.offsetHeight / 2);
}
}, [isMobile, x, y]);
const mask = useMotionTemplate`radial-gradient(${isMobile ? 110 : 250}px at ${x}px ${y}px, white, transparent)`;
const base = "absolute inset-0 rounded-[inherit] transition-opacity duration-300";
return (
<div
ref={ref}
className={cls("group/pattern relative", className)}
onMouseMove={isMobile ? undefined : (e) => {
if (!ref.current) return;
const rect = ref.current.getBoundingClientRect();
x.set(e.clientX - rect.left);
y.set(e.clientY - rect.top);
setChars(randomChars());
}}
>
{children}
<div className="pointer-events-none absolute inset-0 rounded-[inherit]">
<div className={cls(base, isMobile ? "opacity-25" : "opacity-0 group-hover/pattern:opacity-25")} style={{ background: "linear-gradient(white, transparent)" }} />
<motion.div className={cls(base, "bg-linear-to-r from-accent to-accent/50 backdrop-blur-xl", isMobile ? "opacity-100" : "opacity-0 group-hover/pattern:opacity-100")} style={{ maskImage: mask, WebkitMaskImage: mask }} />
<motion.div className={cls(base, "mix-blend-overlay", isMobile ? "opacity-100" : "opacity-0 group-hover/pattern:opacity-100")} style={{ maskImage: mask, WebkitMaskImage: mask }}>
<p className="absolute inset-0 h-full whitespace-pre-wrap wrap-break-word font-mono text-xs font-bold text-white">{chars}</p>
</motion.div>
</div>
</div>
);
};
export default HoverPattern;

View File

@@ -0,0 +1,27 @@
import type { LucideIcon } from "lucide-react";
import { cls } from "@/lib/utils";
const IconTextMarquee = ({ centerIcon: CenterIcon, texts }: { centerIcon: LucideIcon; texts: string[] }) => {
const items = [...texts, ...texts];
return (
<div className="relative flex flex-col h-full w-full overflow-hidden" style={{ maskImage: "radial-gradient(ellipse at center, black 0%, black 30%, transparent 70%)" }}>
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 flex flex-col gap-2 w-full opacity-60">
{Array.from({ length: 10 }).map((_, row) => (
<div key={row} className={cls("flex gap-2", row % 2 === 0 ? "animate-marquee-horizontal" : "animate-marquee-horizontal-reverse")}>
{items.map((text, i) => (
<div key={i} className="flex items-center justify-center px-4 py-2 card rounded">
<p className="text-sm leading-tight">{text}</p>
</div>
))}
</div>
))}
</div>
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-10 flex items-center justify-center h-16 w-16 primary-button backdrop-blur-sm rounded">
<CenterIcon className="h-6 w-6 text-primary-cta-text" strokeWidth={1.5} />
</div>
</div>
);
};
export default IconTextMarquee;

View File

@@ -0,0 +1,41 @@
import { cls } from "@/lib/utils";
interface ImageOrVideoProps {
imageSrc?: string;
videoSrc?: string;
className?: string;
}
const ImageOrVideo = ({
imageSrc,
videoSrc,
className = "",
}: ImageOrVideoProps) => {
if (videoSrc) {
return (
<video
src={videoSrc}
aria-label={videoSrc}
className={cls("w-full h-full min-h-0 object-cover rounded", className)}
autoPlay
loop
muted
playsInline
/>
);
}
if (imageSrc) {
return (
<img
src={imageSrc}
alt={imageSrc}
className={cls("w-full h-full min-h-0 object-cover rounded", className)}
/>
);
}
return null;
};
export default ImageOrVideo;

View File

@@ -0,0 +1,27 @@
import type { LucideIcon } from "lucide-react";
type Item = { icon: LucideIcon; label: string; value: string };
const InfoCardMarquee = ({ items }: { items: Item[] }) => {
const duplicated = [...items, ...items, ...items, ...items];
return (
<div className="h-full overflow-hidden mask-fade-y">
<div className="flex flex-col animate-marquee-vertical">
{duplicated.map((item, i) => (
<div key={i} className="flex items-center justify-between gap-4 p-3 mb-4 card rounded">
<div className="flex items-center gap-3">
<div className="flex items-center justify-center h-10 w-10 secondary-button rounded">
<item.icon className="h-4 w-4 text-secondary-cta-text" strokeWidth={1.5} />
</div>
<p className="text-base truncate">{item.label}</p>
</div>
<p className="text-base">{item.value}</p>
</div>
))}
</div>
</div>
);
};
export default InfoCardMarquee;

View File

@@ -0,0 +1,76 @@
import { Children, useCallback, useEffect, useState, type ReactNode } from "react";
import useEmblaCarousel from "embla-carousel-react";
import type { EmblaCarouselType } from "embla-carousel";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { cls } from "@/lib/utils";
interface LoopCarouselProps {
children: ReactNode;
}
const LoopCarousel = ({ children }: LoopCarouselProps) => {
const [emblaRef, emblaApi] = useEmblaCarousel({ loop: true, align: "center" });
const [selectedIndex, setSelectedIndex] = useState(0);
const items = Children.toArray(children);
const onSelect = useCallback((api: EmblaCarouselType) => {
setSelectedIndex(api.selectedScrollSnap());
}, []);
const scrollPrev = useCallback(() => emblaApi?.scrollPrev(), [emblaApi]);
const scrollNext = useCallback(() => emblaApi?.scrollNext(), [emblaApi]);
useEffect(() => {
if (!emblaApi) return;
onSelect(emblaApi);
emblaApi.on("select", onSelect).on("reInit", onSelect);
return () => {
emblaApi.off("select", onSelect).off("reInit", onSelect);
};
}, [emblaApi, onSelect]);
return (
<div className="relative w-full md:w-content-width mx-auto">
<div ref={emblaRef} className="overflow-hidden w-full mask-fade-x">
<div className="flex w-full">
{items.map((child, index) => (
<div key={index} className="shrink-0 w-content-width md:w-[clamp(18rem,50vw,48rem)] mr-3 md:mr-6">
<div
className={cls(
"transition-all duration-500 ease-out",
selectedIndex === index ? "opacity-100 scale-100" : "opacity-70 scale-90"
)}
>
{child}
</div>
</div>
))}
</div>
</div>
<div className="absolute inset-y-0 left-0 right-0 flex items-center justify-between w-content-width mx-auto pointer-events-none">
<button
onClick={scrollPrev}
type="button"
aria-label="Previous slide"
className="flex items-center justify-center h-8 aspect-square primary-button rounded cursor-pointer pointer-events-auto"
>
<ChevronLeft className="h-2/5 aspect-square text-primary-cta-text" />
</button>
<button
onClick={scrollNext}
type="button"
aria-label="Next slide"
className="flex items-center justify-center h-8 aspect-square primary-button rounded cursor-pointer pointer-events-auto"
>
<ChevronRight className="h-2/5 aspect-square text-primary-cta-text" />
</button>
</div>
</div>
);
};
export default LoopCarousel;

View File

@@ -0,0 +1,32 @@
import ImageOrVideo from "@/components/ui/ImageOrVideo";
import { cls } from "@/lib/utils";
type Item = { imageSrc?: string; videoSrc?: string };
const MediaStack = ({ items }: { items: [Item, Item, Item] }) => (
<div className="group/stack relative flex items-center justify-center h-full w-full rounded select-none card">
<div className={cls(
"absolute z-1 overflow-hidden p-1 w-3/5 aspect-4/3 rounded primary-button",
"translate-x-[12%] -translate-y-[8%] rotate-8 transition-all duration-500",
"group-hover/stack:translate-x-[22%] group-hover/stack:-translate-y-[14%] group-hover/stack:rotate-12"
)}>
<ImageOrVideo imageSrc={items[2].imageSrc} videoSrc={items[2].videoSrc} className="h-full rounded" />
</div>
<div className={cls(
"absolute z-2 overflow-hidden p-1 w-3/5 aspect-4/3 rounded primary-button",
"-translate-x-[12%] -translate-y-[8%] -rotate-8 transition-all duration-500",
"group-hover/stack:-translate-x-[22%] group-hover/stack:-translate-y-[14%] group-hover/stack:-rotate-12"
)}>
<ImageOrVideo imageSrc={items[1].imageSrc} videoSrc={items[1].videoSrc} className="h-full rounded" />
</div>
<div className={cls(
"absolute z-30 overflow-hidden p-1 w-3/5 aspect-4/3 rounded primary-button",
"translate-y-[10%] transition-all duration-500",
"group-hover/stack:translate-y-[20%]"
)}>
<ImageOrVideo imageSrc={items[0].imageSrc} videoSrc={items[0].videoSrc} className="h-full rounded" />
</div>
</div>
);
export default MediaStack;

View File

@@ -0,0 +1,122 @@
import { useState, useEffect } from "react";
import { motion, AnimatePresence } from "motion/react";
import { Plus, ArrowRight } from "lucide-react";
import { cls } from "@/lib/utils";
import Button from "@/components/ui/Button";
interface NavbarCenteredProps {
logo: string;
navItems: { name: string; href: string; id?: string }[];
ctaButton: { text: string; href: string };
}
const handleNavClick = (e: React.MouseEvent<HTMLAnchorElement>, href: string, onClose?: () => void) => {
if (href.startsWith("#")) {
e.preventDefault();
const element = document.getElementById(href.slice(1));
element?.scrollIntoView({ behavior: "smooth", block: "start" });
}
onClose?.();
};
const NavbarCentered = ({ logo, navItems, ctaButton }: NavbarCenteredProps) => {
const [isScrolled, setIsScrolled] = useState(false);
const [menuOpen, setMenuOpen] = useState(false);
useEffect(() => {
const handleScroll = () => setIsScrolled(window.scrollY > 50);
window.addEventListener("scroll", handleScroll, { passive: true });
return () => window.removeEventListener("scroll", handleScroll);
}, []);
return (
<>
<nav
className={cls(
"fixed z-1000 top-0 left-0 w-full transition-all duration-500 ease-in-out",
isScrolled ? "h-15 bg-background/80 backdrop-blur-sm" : "h-20 bg-background/0 backdrop-blur-0"
)}
>
<div className="relative flex items-center justify-between h-full w-content-width mx-auto">
<a href="/" className="text-xl font-medium text-foreground">{logo}</a>
<div className="hidden md:flex absolute left-1/2 items-center gap-6 -translate-x-1/2">
{navItems.map((item) => (
<a
key={item.name}
href={item.href}
onClick={(e) => handleNavClick(e, item.href)}
className="text-base text-foreground hover:opacity-70 transition-opacity"
>
{item.name}
</a>
))}
</div>
<div className="hidden md:block">
<Button text={ctaButton.text} href={ctaButton.href} variant="primary" />
</div>
<button
className="flex md:hidden items-center justify-center shrink-0 h-8 w-8 bg-foreground rounded cursor-pointer"
onClick={() => setMenuOpen(!menuOpen)}
aria-label="Toggle menu"
aria-expanded={menuOpen}
>
<Plus
className={cls("w-1/2 h-1/2 text-background transition-transform duration-300", menuOpen ? "rotate-45" : "rotate-0")}
strokeWidth={1.5}
/>
</button>
</div>
</nav>
<AnimatePresence>
{menuOpen && (
<motion.div
initial={{ y: "-135%" }}
animate={{ y: 0 }}
exit={{ y: "-135%" }}
transition={{ type: "spring", damping: 26, stiffness: 170 }}
className="md:hidden fixed z-1000 top-3 left-3 right-3 p-6 card rounded"
>
<div className="flex items-center justify-between mb-6">
<p className="text-xl text-foreground">Menu</p>
<button
className="flex items-center justify-center shrink-0 h-8 w-8 bg-foreground rounded cursor-pointer"
onClick={() => setMenuOpen(false)}
aria-label="Close menu"
>
<Plus className="w-1/2 h-1/2 text-background rotate-45" strokeWidth={1.5} />
</button>
</div>
<div className="flex flex-col gap-4">
{navItems.map((item, index) => (
<div key={item.name}>
<a
href={item.href}
onClick={(e) => handleNavClick(e, item.href, () => setMenuOpen(false))}
className="flex items-center justify-between py-2 text-base font-medium text-foreground"
>
{item.name}
<ArrowRight className="h-4 w-4 text-foreground" strokeWidth={1.5} />
</a>
{index < navItems.length - 1 && (
<div className="h-px bg-linear-to-r from-transparent via-foreground/20 to-transparent" />
)}
</div>
))}
</div>
<div className="mt-6">
<Button text={ctaButton.text} href={ctaButton.href} variant="primary" className="w-full" />
</div>
</motion.div>
)}
</AnimatePresence>
</>
);
};
export default NavbarCentered;

View File

@@ -0,0 +1,30 @@
import type { LucideIcon } from "lucide-react";
const OrbitingIcons = ({ centerIcon: CenterIcon, items }: { centerIcon: LucideIcon; items: LucideIcon[] }) => (
<div
className="relative flex items-center justify-center h-full overflow-hidden"
style={{ perspective: "2000px", maskImage: "linear-gradient(to bottom, transparent, black 10%, black 90%, transparent), linear-gradient(to right, transparent, black 10%, black 90%, transparent)", maskComposite: "intersect" }}
>
<div className="flex items-center justify-center w-full h-full" style={{ transform: "rotateY(20deg) rotateX(20deg) rotateZ(-20deg)" }}>
<div className="absolute h-60 w-60 opacity-85 border border-background-accent shadow rounded-full" />
<div className="absolute h-80 w-80 opacity-75 border border-background-accent shadow rounded-full" />
<div className="absolute h-100 w-100 opacity-65 border border-background-accent shadow rounded-full" />
<div className="absolute flex items-center justify-center h-40 w-40 border border-background-accent shadow rounded-full">
<div className="flex items-center justify-center h-20 w-20 primary-button rounded-full">
<CenterIcon className="h-10 w-10 text-primary-cta-text" strokeWidth={1.25} />
</div>
{items.map((Icon, i) => (
<div
key={i}
className="absolute flex items-center justify-center h-10 w-10 rounded shadow card -ml-5 -mt-5"
style={{ top: "50%", left: "50%", animation: "orbit 12s linear infinite", "--initial-position": `${(360 / items.length) * i}deg`, "--translate-position": "160px" } as React.CSSProperties}
>
<Icon className="h-4 w-4" strokeWidth={1.5} />
</div>
))}
</div>
</div>
</div>
);
export default OrbitingIcons;

View File

@@ -0,0 +1,60 @@
import { motion } from "motion/react";
type Variant = "slide-up" | "fade-blur" | "fade";
interface TextAnimationProps {
text: string;
variant: Variant;
tag?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div";
className?: string;
}
const VARIANTS = {
"slide-up": {
hidden: { opacity: 0, y: "50%" },
visible: { opacity: 1, y: 0 },
},
"fade-blur": {
hidden: { opacity: 0, filter: "blur(10px)" },
visible: { opacity: 1, filter: "blur(0px)" },
},
"fade": {
hidden: { opacity: 0 },
visible: { opacity: 1 },
},
};
const EASING: Record<Variant, [number, number, number, number]> = {
"slide-up": [0.25, 0.46, 0.45, 0.94],
"fade-blur": [0.45, 0, 0.55, 1],
"fade": [0.45, 0, 0.55, 1],
};
const TextAnimation = ({ text, variant, tag = "p", className = "" }: TextAnimationProps) => {
const Tag = motion[tag] as typeof motion.p;
const words = text.split(" ");
return (
<Tag
className={className}
initial="hidden"
whileInView="visible"
viewport={{ once: true, margin: "-20%" }}
transition={{ staggerChildren: 0.04 }}
>
{words.map((word, i) => (
<motion.span
key={i}
className="inline-block"
variants={VARIANTS[variant]}
transition={{ duration: 0.6, ease: EASING[variant] }}
>
{word}
{i < words.length - 1 && "\u00A0"}
</motion.span>
))}
</Tag>
);
};
export default TextAnimation;

View File

@@ -0,0 +1,28 @@
import type { LucideIcon } from "lucide-react";
import { cls } from "@/lib/utils";
type Item = { icon: LucideIcon; title: string; subtitle: string; detail: string };
const POS = ["-translate-y-14 hover:-translate-y-20", "translate-x-16 hover:-translate-y-4", "translate-x-32 translate-y-16 hover:translate-y-10"];
const TiltedStackCards = ({ items }: { items: [Item, Item, Item] }) => (
<div
className="h-full grid place-items-center [grid-template-areas:'stack']"
style={{ maskImage: "linear-gradient(to bottom, transparent, black 10%, black 90%, transparent), linear-gradient(to right, black, black 80%, transparent)", maskComposite: "intersect" }}
>
{items.map((item, i) => (
<div key={i} className={cls("flex flex-col justify-between gap-2 p-6 w-80 h-36 card rounded transition-all duration-500 -skew-y-[8deg] [grid-area:stack] 2xl:w-90", POS[i])}>
<div className="flex items-center gap-2">
<div className="flex items-center justify-center h-5 w-5 rounded primary-button">
<item.icon className="h-3 w-3 text-primary-cta-text" strokeWidth={1.5} />
</div>
<p className="text-base">{item.title}</p>
</div>
<p className="text-lg whitespace-nowrap">{item.subtitle}</p>
<p className="text-base">{item.detail}</p>
</div>
))}
</div>
);
export default TiltedStackCards;

View File

@@ -0,0 +1,37 @@
import { motion } from "motion/react";
import type { ReactNode } from "react";
interface TransitionProps {
children: ReactNode;
className?: string;
transitionType?: "full" | "fade";
whileInView?: boolean;
}
const Transition = ({
children,
className = "flex flex-col w-full gap-6",
transitionType = "full",
whileInView = true,
}: TransitionProps) => {
const initial = transitionType === "full"
? { opacity: 0, y: 20 }
: { opacity: 0 };
const target = transitionType === "full"
? { opacity: 1, y: 0 }
: { opacity: 1 };
return (
<motion.div
initial={initial}
{...(whileInView ? { whileInView: target, viewport: { once: true, margin: "-15%" } } : { animate: target })}
transition={{ duration: 0.6, ease: "easeOut" }}
className={className}
>
{children}
</motion.div>
);
};
export default Transition;

View File

@@ -0,0 +1,51 @@
import { useNavigate, useLocation } from "react-router-dom";
export const useButtonClick = (href?: string, onClick?: () => void) => {
const navigate = useNavigate();
const location = useLocation();
const scrollToElement = (sectionId: string, delay: number = 100) => {
setTimeout(() => {
const element = document.getElementById(sectionId);
if (element) {
element.scrollIntoView({ behavior: "smooth", block: "start" });
}
}, delay);
};
const handleClick = () => {
if (href) {
const isExternalLink = /^(https?:\/\/|www\.)/.test(href);
if (isExternalLink) {
window.open(
href.startsWith("www.") ? `https://${href}` : href,
"_blank",
"noopener,noreferrer"
);
} else if (href.startsWith("/")) {
const [path, hash] = href.split("#");
if (path !== location.pathname) {
navigate(path);
if (hash) {
setTimeout(() => {
window.location.hash = hash;
scrollToElement(hash, 100);
}, 100);
}
} else if (hash) {
window.location.hash = hash;
scrollToElement(hash, 50);
}
} else if (href.startsWith("#")) {
scrollToElement(href.slice(1), 50);
} else {
scrollToElement(href, 50);
}
}
onClick?.();
};
return handleClick;
};

View File

@@ -0,0 +1,45 @@
import { useCallback, useEffect, useState } from "react";
import type { EmblaCarouselType } from "embla-carousel";
export const useCarouselControls = (emblaApi: EmblaCarouselType | undefined) => {
const [prevDisabled, setPrevDisabled] = useState(true);
const [nextDisabled, setNextDisabled] = useState(true);
const [scrollProgress, setScrollProgress] = useState(0);
const scrollPrev = useCallback(() => {
if (!emblaApi) return;
emblaApi.scrollPrev();
}, [emblaApi]);
const scrollNext = useCallback(() => {
if (!emblaApi) return;
emblaApi.scrollNext();
}, [emblaApi]);
const onSelect = useCallback((api: EmblaCarouselType) => {
setPrevDisabled(!api.canScrollPrev());
setNextDisabled(!api.canScrollNext());
}, []);
const onScroll = useCallback((api: EmblaCarouselType) => {
const progress = Math.max(0, Math.min(1, api.scrollProgress()));
setScrollProgress(progress * 100);
}, []);
useEffect(() => {
if (!emblaApi) return;
onSelect(emblaApi);
onScroll(emblaApi);
emblaApi.on("reInit", onSelect).on("select", onSelect);
emblaApi.on("reInit", onScroll).on("scroll", onScroll);
return () => {
emblaApi.off("reInit", onSelect).off("select", onSelect);
emblaApi.off("reInit", onScroll).off("scroll", onScroll);
};
}, [emblaApi, onSelect, onScroll]);
return { prevDisabled, nextDisabled, scrollPrev, scrollNext, scrollProgress };
};

171
src/index.css Normal file
View File

@@ -0,0 +1,171 @@
@import "tailwindcss";
@import "./styles/masks.css";
@import "./styles/animations.css";
:root {
/* @colorThemes/lightTheme/grayBlueAccent */
--background: #ffffff;
--card: #f9f9f9;
--foreground: #000612e6;
--primary-cta: #15479c;
--primary-cta-text: #ffffff;
--secondary-cta: #f9f9f9;
--secondary-cta-text: #000612e6;
--accent: #e2e2e2;
--background-accent: #c4c4c4;
/* @layout/border-radius/rounded */
--radius: 9999px;
/* @layout/content-width/medium */
--width-content-width: clamp(40rem, 80vw, 100rem);
/* @utilities/masks */
--vw-1_5: 1.5vw;
--width-x-padding-mask-fade: 5vw;
/* @layout/carousel */
--width-carousel-padding: calc((100vw - var(--width-content-width)) / 2 + 1px - var(--vw-1_5));
--width-carousel-padding-controls: calc((100vw - var(--width-content-width)) / 2 + 1px);
--width-carousel-item-2: calc(var(--width-content-width) / 2 - var(--vw-1_5) / 2);
--width-carousel-item-3: calc(var(--width-content-width) / 3 - var(--vw-1_5) / 3 * 2);
--width-carousel-item-4: calc(var(--width-content-width) / 4 - var(--vw-1_5) / 4 * 3);
/* @typography/text-sizing/medium */
--text-2xs: clamp(0.465rem, 0.62vw, 0.62rem);
--text-xs: clamp(0.54rem, 0.72vw, 0.72rem);
--text-sm: clamp(0.615rem, 0.82vw, 0.82rem);
--text-base: clamp(0.69rem, 0.92vw, 0.92rem);
--text-lg: clamp(0.75rem, 1vw, 1rem);
--text-xl: clamp(0.825rem, 1.1vw, 1.1rem);
--text-2xl: clamp(0.975rem, 1.3vw, 1.3rem);
--text-3xl: clamp(1.2rem, 1.6vw, 1.6rem);
--text-4xl: clamp(1.5rem, 2vw, 2rem);
--text-5xl: clamp(2.025rem, 2.75vw, 2.75rem);
--text-6xl: clamp(2.475rem, 3.3vw, 3.3rem);
--text-7xl: clamp(3rem, 4vw, 4rem);
--text-8xl: clamp(3.5rem, 4.5vw, 4.5rem);
--text-9xl: clamp(5.25rem, 7vw, 7rem);
}
/* @typography/text-sizing/medium (mobile) */
@media (max-width: 768px) {
:root {
--text-2xs: 2.5vw;
--text-xs: 2.75vw;
--text-sm: 3vw;
--text-base: 3.25vw;
--text-lg: 3.5vw;
--text-xl: 4.25vw;
--text-2xl: 5vw;
--text-3xl: 6vw;
--text-4xl: 7vw;
--text-5xl: 7.5vw;
--text-6xl: 8.5vw;
--text-7xl: 10vw;
--text-8xl: 12vw;
--text-9xl: 14vw;
--width-content-width: 80vw;
--width-carousel-padding: calc((100vw - var(--width-content-width)) / 2 + 1px - var(--vw-1_5));
--width-carousel-padding-controls: calc((100vw - var(--width-content-width)) / 2 + 1px);
--width-carousel-item-2: var(--width-content-width);
--width-carousel-item-3: var(--width-content-width);
--width-carousel-item-4: var(--width-content-width);
}
}
@theme inline {
/* Colors */
--color-background: var(--background);
--color-card: var(--card);
--color-foreground: var(--foreground);
--color-primary-cta: var(--primary-cta);
--color-primary-cta-text: var(--primary-cta-text);
--color-secondary-cta: var(--secondary-cta);
--color-secondary-cta-text: var(--secondary-cta-text);
--color-accent: var(--accent);
--color-background-accent: var(--background-accent);
/* Fonts */
--font-sans: "Inter", sans-serif;
--font-mono: monospace;
/* Border Radius */
--radius: var(--radius);
--radius-lg: var(--radius);
--radius-md: calc(var(--radius) - 2px);
--radius-sm: calc(var(--radius) - 4px);
/* Width */
--width-content-width: var(--width-content-width);
--width-carousel-padding: var(--width-carousel-padding);
--width-carousel-padding-controls: var(--width-carousel-padding-controls);
--width-carousel-item-2: var(--width-carousel-item-2);
--width-carousel-item-3: var(--width-carousel-item-3);
--width-carousel-item-4: var(--width-carousel-item-4);
/* Typography */
--text-2xs: var(--text-2xs);
--text-xs: var(--text-xs);
--text-sm: var(--text-sm);
--text-base: var(--text-base);
--text-lg: var(--text-lg);
--text-xl: var(--text-xl);
--text-2xl: var(--text-2xl);
--text-3xl: var(--text-3xl);
--text-4xl: var(--text-4xl);
--text-5xl: var(--text-5xl);
--text-6xl: var(--text-6xl);
--text-7xl: var(--text-7xl);
--text-8xl: var(--text-8xl);
--text-9xl: var(--text-9xl);
}
* {
scrollbar-width: thin;
scrollbar-color: rgba(255, 255, 255, 1) rgba(255, 255, 255, 0);
}
html {
overscroll-behavior: none;
overscroll-behavior-y: none;
}
body {
margin: 0;
background-color: var(--background);
color: var(--foreground);
font-family: "Inter", sans-serif;
position: relative;
min-height: 100vh;
overscroll-behavior: none;
overscroll-behavior-y: none;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: "Inter", sans-serif;
}
/* WEBILD_CARD_STYLE */
/* @cards/soft-shadow */
.card {
background: var(--color-card);
box-shadow: color-mix(in srgb, var(--color-accent) 10%, transparent) 0px 0.706592px 0.706592px -0.666667px, color-mix(in srgb, var(--color-accent) 8%, transparent) 0px 1.80656px 1.80656px -1.33333px, color-mix(in srgb, var(--color-accent) 7%, transparent) 0px 3.62176px 3.62176px -2px, color-mix(in srgb, var(--color-accent) 7%, transparent) 0px 6.8656px 6.8656px -2.66667px, color-mix(in srgb, var(--color-accent) 5%, transparent) 0px 13.6468px 13.6468px -3.33333px, color-mix(in srgb, var(--color-accent) 2%, transparent) 0px 30px 30px -4px, var(--color-background) 0px 3px 1px 0px inset;
}
/* WEBILD_PRIMARY_BUTTON */
/* @primaryButtons/flat */
.primary-button {
background: var(--color-primary-cta);
}
/* WEBILD_SECONDARY_BUTTON */
/* @secondaryButtons/solid */
.secondary-button {
background: var(--color-secondary-cta);
}

10
src/lib/utils.ts Normal file
View File

@@ -0,0 +1,10 @@
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: (string | undefined | null | false)[]) {
return inputs.filter(Boolean).join(" ");
}
export function cls(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

13
src/main.tsx Normal file
View File

@@ -0,0 +1,13 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'
import './index.css'
import App from './App.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>,
)

View File

@@ -0,0 +1,25 @@
import { Link } from "react-router-dom";
const categories = [
{ title: "Sections", href: "/components/sections" },
];
const ComponentsPage = () => {
return (
<section className="min-h-screen py-16">
<div className="w-content-width mx-auto">
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
{categories.map((category) => (
<Link key={category.href} to={category.href}>
<div className="relative flex items-center justify-center aspect-square p-6 text-center card rounded-lg cursor-pointer hover:opacity-80 transition-opacity">
<h3 className="text-xl font-normal">{category.title}</h3>
</div>
</Link>
))}
</div>
</div>
</section>
);
};
export default ComponentsPage;

View File

@@ -0,0 +1,37 @@
import { Link } from "react-router-dom";
const sectionComponents = [
{ title: "Hero", href: "/components/sections/hero" },
{ title: "About", href: "/components/sections/about" },
{ title: "Features", href: "/components/sections/features" },
{ title: "Pricing", href: "/components/sections/pricing" },
{ title: "Metrics", href: "/components/sections/metrics" },
{ title: "Team", href: "/components/sections/team" },
{ title: "Testimonial", href: "/components/sections/testimonial" },
{ title: "Social Proof", href: "/components/sections/social-proof" },
{ title: "FAQ", href: "/components/sections/faq" },
{ title: "Blog", href: "/components/sections/blog" },
{ title: "Contact", href: "/components/sections/contact" },
{ title: "Footer", href: "/components/sections/footer" },
{ title: "Legal", href: "/components/sections/legal" },
];
const SectionsPage = () => {
return (
<section className="min-h-screen py-16">
<div className="w-content-width mx-auto">
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
{sectionComponents.map((component) => (
<Link key={component.href} to={component.href}>
<div className="relative flex items-center justify-center aspect-square p-6 text-center card rounded-lg cursor-pointer hover:opacity-80 transition-opacity">
<h3 className="text-xl font-normal">{component.title}</h3>
</div>
</Link>
))}
</div>
</div>
</section>
);
};
export default SectionsPage;

View File

@@ -0,0 +1,33 @@
import { Zap, Shield, Sparkles } from "lucide-react";
import NavbarCentered from "@/components/ui/NavbarCentered";
import AboutFeaturesSplit from "@/components/sections/about/AboutFeaturesSplit";
const AboutFeaturesSplitPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<AboutFeaturesSplit
tag="About Us"
title="We Build Digital Experiences That Matter"
description="Our team combines creativity with technical expertise to deliver solutions that drive real results for your business"
primaryButton={{ text: "Learn More", href: "#" }}
items={[
{ icon: Zap, title: "Lightning Fast", description: "Optimized performance that keeps your users engaged" },
{ icon: Shield, title: "Secure by Default", description: "Built with security best practices from the ground up" },
{ icon: Sparkles, title: "Modern Design", description: "Clean, contemporary interfaces that stand out" },
]}
imageSrc="/placeholders/placeholder.webp"
/>
</>
);
export default AboutFeaturesSplitPage;

View File

@@ -0,0 +1,28 @@
import { Link } from "react-router-dom";
const aboutComponents = [
{ title: "About Features Split", href: "/components/sections/about/features-split" },
{ title: "About Text Split", href: "/components/sections/about/text-split" },
{ title: "About Testimonial", href: "/components/sections/about/testimonial" },
{ title: "About Media Overlay", href: "/components/sections/about/media-overlay" },
];
const AboutListPage = () => {
return (
<section className="min-h-screen py-16">
<div className="w-content-width mx-auto">
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
{aboutComponents.map((component) => (
<Link key={component.href} to={component.href}>
<div className="relative flex items-center justify-center aspect-square p-6 text-center card rounded-lg cursor-pointer hover:opacity-80 transition-opacity">
<h3 className="text-xl font-normal">{component.title}</h3>
</div>
</Link>
))}
</div>
</div>
</section>
);
};
export default AboutListPage;

View File

@@ -0,0 +1,28 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import AboutMediaOverlay from "@/components/sections/about/AboutMediaOverlay";
const AboutMediaOverlayPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<AboutMediaOverlay
tag="About Us"
title="Crafting Digital Excellence"
description="We transform ideas into exceptional digital experiences that captivate and inspire"
primaryButton={{ text: "Get Started", href: "#" }}
secondaryButton={{ text: "Learn More", href: "#" }}
imageSrc="/placeholders/placeholder.webp"
/>
</>
);
export default AboutMediaOverlayPage;

View File

@@ -0,0 +1,27 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import AboutTestimonial from "@/components/sections/about/AboutTestimonial";
const AboutTestimonialPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<AboutTestimonial
tag="Testimonial"
quote="Working with this team transformed our business. Their attention to detail and commitment to excellence exceeded all expectations."
author="Sarah Johnson"
role="CEO at TechCorp"
imageSrc="/placeholders/placeholder.webp"
/>
</>
);
export default AboutTestimonialPage;

View File

@@ -0,0 +1,29 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import AboutTextSplit from "@/components/sections/about/AboutTextSplit";
const AboutTextSplitPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<AboutTextSplit
title="We Create Digital Products That People Love"
descriptions={[
"Our approach combines strategic thinking with creative execution to deliver meaningful results.",
"We believe in building lasting partnerships with our clients, working together to achieve their goals.",
]}
primaryButton={{ text: "Get Started", href: "#" }}
secondaryButton={{ text: "Learn More", href: "#" }}
/>
</>
);
export default AboutTextSplitPage;

View File

@@ -0,0 +1,27 @@
import { Link } from "react-router-dom";
const blogComponents = [
{ title: "Blog Simple Cards", href: "/components/sections/blog/simple-cards" },
{ title: "Blog Tag Cards", href: "/components/sections/blog/tag-cards" },
{ title: "Blog Media Cards", href: "/components/sections/blog/media-cards" },
];
const BlogListPage = () => {
return (
<section className="min-h-screen py-16">
<div className="w-content-width mx-auto">
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
{blogComponents.map((component) => (
<Link key={component.href} to={component.href}>
<div className="relative flex items-center justify-center aspect-square p-6 text-center card rounded-lg cursor-pointer hover:opacity-80 transition-opacity">
<h3 className="text-xl font-normal">{component.title}</h3>
</div>
</Link>
))}
</div>
</div>
</section>
);
};
export default BlogListPage;

View File

@@ -0,0 +1,58 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import BlogMediaCards from "@/components/sections/blog/BlogMediaCards";
const BlogMediaCardsPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<BlogMediaCards
tag="Blog"
title="Latest articles"
description="Insights and updates from our team on design, development, and building great products."
items={[
{
category: "Design",
title: "The future of design systems",
excerpt: "How modern design systems are evolving to meet the needs of growing teams and complex products.",
authorName: "Sarah Chen",
authorImageSrc: "https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=100",
date: "Mar 15, 2024",
imageSrc: "https://images.unsplash.com/photo-1558655146-9f40138edfeb?w=800",
href: "/blog/design-systems",
},
{
category: "Development",
title: "Building scalable React apps",
excerpt: "Best practices for structuring your React applications for long-term maintainability.",
authorName: "Alex Rivera",
authorImageSrc: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100",
date: "Mar 12, 2024",
imageSrc: "https://images.unsplash.com/photo-1555066931-4365d14bab8c?w=800",
href: "/blog/scalable-react",
},
{
category: "Product",
title: "User research that matters",
excerpt: "How to conduct effective user research that actually influences product decisions.",
authorName: "Maya Patel",
authorImageSrc: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=100",
date: "Mar 10, 2024",
imageSrc: "https://images.unsplash.com/photo-1552664730-d307ca884978?w=800",
href: "/blog/user-research",
},
]}
/>
<div className="h-[50vh]" />
</>
);
export default BlogMediaCardsPage;

View File

@@ -0,0 +1,58 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import BlogSimpleCards from "@/components/sections/blog/BlogSimpleCards";
const BlogSimpleCardsPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<BlogSimpleCards
tag="Blog"
title="Latest articles"
description="Insights and updates from our team on design, development, and building great products."
items={[
{
category: "Design",
title: "The future of design systems",
excerpt: "How modern design systems are evolving to meet the needs of growing teams and complex products.",
authorName: "Sarah Chen",
authorImageSrc: "https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=100",
date: "Mar 15, 2024",
imageSrc: "https://images.unsplash.com/photo-1558655146-9f40138edfeb?w=800",
href: "/blog/design-systems",
},
{
category: "Development",
title: "Building scalable React apps",
excerpt: "Best practices for structuring your React applications for long-term maintainability.",
authorName: "Alex Rivera",
authorImageSrc: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100",
date: "Mar 12, 2024",
imageSrc: "https://images.unsplash.com/photo-1555066931-4365d14bab8c?w=800",
href: "/blog/scalable-react",
},
{
category: "Product",
title: "User research that matters",
excerpt: "How to conduct effective user research that actually influences product decisions.",
authorName: "Maya Patel",
authorImageSrc: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=100",
date: "Mar 10, 2024",
imageSrc: "https://images.unsplash.com/photo-1552664730-d307ca884978?w=800",
href: "/blog/user-research",
},
]}
/>
<div className="h-[50vh]" />
</>
);
export default BlogSimpleCardsPage;

View File

@@ -0,0 +1,58 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import BlogTagCards from "@/components/sections/blog/BlogTagCards";
const BlogTagCardsPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<BlogTagCards
tag="Blog"
title="Latest articles"
description="Insights and updates from our team on design, development, and building great products."
items={[
{
title: "The future of design systems",
excerpt: "How modern design systems are evolving to meet the needs of growing teams and complex products.",
authorName: "Sarah Chen",
authorImageSrc: "https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=100",
date: "Mar 15, 2024",
imageSrc: "https://images.unsplash.com/photo-1558655146-9f40138edfeb?w=800",
tags: ["Design", "Systems"],
href: "/blog/design-systems",
},
{
title: "Building scalable React apps",
excerpt: "Best practices for structuring your React applications for long-term maintainability.",
authorName: "Alex Rivera",
authorImageSrc: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100",
date: "Mar 12, 2024",
imageSrc: "https://images.unsplash.com/photo-1555066931-4365d14bab8c?w=800",
tags: ["React", "Development", "Frontend"],
href: "/blog/scalable-react",
},
{
title: "User research that matters",
excerpt: "How to conduct effective user research that actually influences product decisions.",
authorName: "Maya Patel",
authorImageSrc: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=100",
date: "Mar 10, 2024",
imageSrc: "https://images.unsplash.com/photo-1552664730-d307ca884978?w=800",
tags: ["Research", "UX"],
href: "/blog/user-research",
},
]}
/>
<div className="h-[50vh]" />
</>
);
export default BlogTagCardsPage;

View File

@@ -0,0 +1,30 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import ContactCenter from "@/components/sections/contact/ContactCenter";
const ContactCenterPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<ContactCenter
tag="Contact"
title="Get in touch"
description="Subscribe to our newsletter and stay updated with the latest news, features, and exclusive offers."
inputPlaceholder="Enter your email"
buttonText="Subscribe"
termsText="By subscribing you agree to our Terms of Service and Privacy Policy."
onSubmit={(email) => console.log("Email submitted:", email)}
/>
<div className="h-[50vh]" />
</>
);
export default ContactCenterPage;

View File

@@ -0,0 +1,27 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import ContactCta from "@/components/sections/contact/ContactCta";
const ContactCtaPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<ContactCta
tag="Contact"
text="Ready to transform your business? Let's start a conversation and explore how we can help you achieve your goals."
primaryButton={{ text: "Get in Touch", href: "#" }}
secondaryButton={{ text: "Learn More", href: "#" }}
/>
<div className="h-[50vh]" />
</>
);
export default ContactCtaPage;

View File

@@ -0,0 +1,28 @@
import { Link } from "react-router-dom";
const contactComponents = [
{ title: "Contact Center", href: "/components/sections/contact/center" },
{ title: "Contact Split Email", href: "/components/sections/contact/split-email" },
{ title: "Contact Split Form", href: "/components/sections/contact/split-form" },
{ title: "Contact CTA", href: "/components/sections/contact/cta" },
];
const ContactListPage = () => {
return (
<section className="min-h-screen py-16">
<div className="w-content-width mx-auto">
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
{contactComponents.map((component) => (
<Link key={component.href} to={component.href}>
<div className="relative flex items-center justify-center aspect-square p-6 text-center card rounded-lg cursor-pointer hover:opacity-80 transition-opacity">
<h3 className="text-xl font-normal">{component.title}</h3>
</div>
</Link>
))}
</div>
</div>
</section>
);
};
export default ContactListPage;

View File

@@ -0,0 +1,31 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import ContactSplitEmail from "@/components/sections/contact/ContactSplitEmail";
const ContactSplitEmailPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<ContactSplitEmail
tag="Contact"
title="Get in touch"
description="Subscribe to our newsletter and stay updated with the latest news and exclusive offers."
inputPlaceholder="Enter your email"
buttonText="Subscribe"
termsText="By subscribing you agree to our Terms of Service and Privacy Policy."
imageSrc="https://images.unsplash.com/photo-1497366216548-37526070297c?w=800"
onSubmit={(email) => console.log("Email submitted:", email)}
/>
<div className="h-[50vh]" />
</>
);
export default ContactSplitEmailPage;

View File

@@ -0,0 +1,39 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import ContactSplitForm from "@/components/sections/contact/ContactSplitForm";
const ContactSplitFormPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<ContactSplitForm
tag="Contact"
title="Get in touch"
description="Fill out the form below and we'll get back to you as soon as possible."
inputs={[
{ name: "name", type: "text", placeholder: "Your name", required: true },
{ name: "email", type: "email", placeholder: "Your email", required: true },
]}
textarea={{
name: "message",
placeholder: "Your message",
rows: 5,
required: true,
}}
buttonText="Send Message"
imageSrc="https://images.unsplash.com/photo-1497366216548-37526070297c?w=800"
onSubmit={(data) => console.log("Form submitted:", data)}
/>
<div className="h-[50vh]" />
</>
);
export default ContactSplitFormPage;

View File

@@ -0,0 +1,27 @@
import { Link } from "react-router-dom";
const faqComponents = [
{ title: "FAQ Simple", href: "/components/sections/faq/simple" },
{ title: "FAQ Two Column", href: "/components/sections/faq/two-column" },
{ title: "FAQ Split Media", href: "/components/sections/faq/split-media" },
];
const FaqListPage = () => {
return (
<section className="min-h-screen py-16">
<div className="w-content-width mx-auto">
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
{faqComponents.map((component) => (
<Link key={component.href} to={component.href}>
<div className="relative flex items-center justify-center aspect-square p-6 text-center card rounded-lg cursor-pointer hover:opacity-80 transition-opacity">
<h3 className="text-xl font-normal">{component.title}</h3>
</div>
</Link>
))}
</div>
</div>
</section>
);
};
export default FaqListPage;

View File

@@ -0,0 +1,48 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FaqSimple from "@/components/sections/faq/FaqSimple";
const FaqSimplePage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FaqSimple
tag="FAQ"
title="Frequently asked questions"
description="Everything you need to know about our product and billing. Can't find the answer you're looking for? Please chat with our friendly team."
items={[
{
question: "What payment methods do you accept?",
answer: "We accept all major credit cards including Visa, Mastercard, and American Express. We also support PayPal and bank transfers for annual subscriptions.",
},
{
question: "Can I change my plan later?",
answer: "Yes, you can upgrade or downgrade your plan at any time. Changes will be reflected in your next billing cycle, and we'll prorate any differences.",
},
{
question: "Is there a free trial available?",
answer: "Yes, we offer a 14-day free trial for all new users. No credit card required. You'll have full access to all features during the trial period.",
},
{
question: "How do I cancel my subscription?",
answer: "You can cancel your subscription at any time from your account settings. Your access will continue until the end of your current billing period.",
},
{
question: "Do you offer refunds?",
answer: "We offer a 30-day money-back guarantee for all new subscriptions. If you're not satisfied, contact our support team for a full refund.",
},
]}
/>
<div className="h-[50vh]" />
</>
);
export default FaqSimplePage;

View File

@@ -0,0 +1,49 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FaqSplitMedia from "@/components/sections/faq/FaqSplitMedia";
const FaqSplitMediaPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FaqSplitMedia
tag="FAQ"
title="Frequently asked questions"
description="Everything you need to know about our product and billing. Can't find the answer you're looking for? Please chat with our friendly team."
imageSrc="https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=800"
items={[
{
question: "What payment methods do you accept?",
answer: "We accept all major credit cards including Visa, Mastercard, and American Express. We also support PayPal and bank transfers for annual subscriptions.",
},
{
question: "Can I change my plan later?",
answer: "Yes, you can upgrade or downgrade your plan at any time. Changes will be reflected in your next billing cycle, and we'll prorate any differences.",
},
{
question: "Is there a free trial available?",
answer: "Yes, we offer a 14-day free trial for all new users. No credit card required. You'll have full access to all features during the trial period.",
},
{
question: "How do I cancel my subscription?",
answer: "You can cancel your subscription at any time from your account settings. Your access will continue until the end of your current billing period.",
},
{
question: "Do you offer refunds?",
answer: "We offer a 30-day money-back guarantee for all new subscriptions. If you're not satisfied, contact our support team for a full refund.",
},
]}
/>
<div className="h-[50vh]" />
</>
);
export default FaqSplitMediaPage;

View File

@@ -0,0 +1,52 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FaqTwoColumn from "@/components/sections/faq/FaqTwoColumn";
const FaqTwoColumnPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FaqTwoColumn
tag="FAQ"
title="Frequently asked questions"
description="Everything you need to know about our product and billing. Can't find the answer you're looking for? Please chat with our friendly team."
items={[
{
question: "What payment methods do you accept?",
answer: "We accept all major credit cards including Visa, Mastercard, and American Express. We also support PayPal and bank transfers for annual subscriptions.",
},
{
question: "Can I change my plan later?",
answer: "Yes, you can upgrade or downgrade your plan at any time. Changes will be reflected in your next billing cycle, and we'll prorate any differences.",
},
{
question: "Is there a free trial available?",
answer: "Yes, we offer a 14-day free trial for all new users. No credit card required. You'll have full access to all features during the trial period.",
},
{
question: "How do I cancel my subscription?",
answer: "You can cancel your subscription at any time from your account settings. Your access will continue until the end of your current billing period.",
},
{
question: "Do you offer refunds?",
answer: "We offer a 30-day money-back guarantee for all new subscriptions. If you're not satisfied, contact our support team for a full refund.",
},
{
question: "Can I invite team members?",
answer: "Yes, you can invite unlimited team members on our Pro and Enterprise plans. Each team member gets their own login and personalized dashboard.",
},
]}
/>
<div className="h-[50vh]" />
</>
);
export default FaqTwoColumnPage;

View File

@@ -0,0 +1,46 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FeaturesAlternatingSplit from "@/components/sections/features/FeaturesAlternatingSplit";
const FeaturesAlternatingSplitPage = () => {
return (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FeaturesAlternatingSplit
tag="Features"
title="Everything You Need to Succeed"
description="Powerful features designed to help you build better products faster"
primaryButton={{ text: "Get Started", href: "#" }}
items={[
{
title: "Lightning Fast Performance",
description: "Optimized for speed and performance. Our platform is built to handle millions of requests without breaking a sweat.",
imageSrc: "/placeholders/placeholder.webp",
},
{
title: "Secure by Default",
description: "Built with security best practices from the ground up. Your data is always protected with enterprise-grade encryption.",
imageSrc: "/placeholders/placeholder.webp",
},
{
title: "Easy Integration",
description: "Works seamlessly with your existing stack. Connect with popular tools and services in just a few clicks.",
imageSrc: "/placeholders/placeholder.webp",
primaryButton: { text: "View Integrations", href: "#" },
},
]}
/>
</>
);
};
export default FeaturesAlternatingSplitPage;

View File

@@ -0,0 +1,51 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FeaturesArrowCards from "@/components/sections/features/FeaturesArrowCards";
const FeaturesArrowCardsPage = () => {
return (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FeaturesArrowCards
tag="Projects"
title="Our Latest Work"
description="Explore our portfolio of successful projects across various industries and technologies"
primaryButton={{ text: "View All Projects", href: "#" }}
secondaryButton={{ text: "Contact Us", href: "#" }}
items={[
{
title: "E-commerce Platform Redesign",
tags: ["Web Design", "Development", "UX"],
imageSrc: "/placeholders/placeholder.webp",
},
{
title: "Mobile Banking Application",
tags: ["Mobile", "FinTech"],
imageSrc: "/placeholders/placeholder.webp",
},
{
title: "Healthcare Management System",
tags: ["SaaS", "Healthcare", "AI"],
imageSrc: "/placeholders/placeholder.webp",
},
{
title: "Real Estate Marketplace",
tags: ["Marketplace", "PropTech"],
imageSrc: "/placeholders/placeholder.webp",
},
]}
/>
</>
);
};
export default FeaturesArrowCardsPage;

View File

@@ -0,0 +1,105 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FeaturesBento from "@/components/sections/features/FeaturesBento";
import { Zap, Shield, BarChart, Users, Bell, Mail, Bot } from "lucide-react";
const FeaturesBentoPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FeaturesBento
tag="Features"
title="Powerful Features for Modern Teams"
description="Everything you need to build amazing products, all in one place."
primaryButton={{ text: "Get Started", href: "#" }}
secondaryButton={{ text: "Learn More", href: "#" }}
features={[
{
bentoComponent: "info-card-marquee",
title: "Real-time Analytics",
description: "Track your metrics in real-time with our powerful dashboard.",
items: [
{ icon: Zap, label: "Active Users", value: "12.5K" },
{ icon: Shield, label: "Uptime", value: "99.9%" },
{ icon: BarChart, label: "Growth", value: "+24%" },
{ icon: Users, label: "Team Size", value: "48" },
],
},
{
bentoComponent: "animated-bar-chart",
title: "Performance Metrics",
description: "Visualize your data with beautiful animated charts.",
},
{
bentoComponent: "tilted-stack-cards",
title: "Key Insights",
description: "Discover actionable insights from your data.",
items: [
{ icon: Zap, title: "Revenue", subtitle: "$124,500", detail: "+12% this month" },
{ icon: Users, title: "Users", subtitle: "48,200", detail: "+8% this week" },
{ icon: BarChart, title: "Growth", subtitle: "24%", detail: "Above target" },
],
},
{
bentoComponent: "orbiting-icons",
title: "Integrations",
description: "Connect with all your favorite tools and services.",
centerIcon: Zap,
items: [Shield, BarChart, Users, Bell, Mail],
},
{
bentoComponent: "chat-marquee",
title: "AI Assistant",
description: "Get instant help from our intelligent assistant.",
aiIcon: Bot,
userIcon: Users,
exchanges: [
{ userMessage: "How can I improve my metrics?", aiResponse: "Based on your data, I recommend focusing on user engagement." },
{ userMessage: "Show me the trends", aiResponse: "Your growth has increased 24% this month!" },
],
placeholder: "Ask me anything...",
},
{
bentoComponent: "checklist-timeline",
title: "Project Progress",
description: "Track your team's progress in real-time.",
heading: "Sprint 12",
subheading: "In Progress",
items: [
{ label: "Design Review", detail: "Completed" },
{ label: "Development", detail: "In Progress" },
{ label: "QA Testing", detail: "Pending" },
],
completedLabel: "All tasks completed!",
},
{
bentoComponent: "icon-text-marquee",
title: "Tech Stack",
description: "Built with the best technologies.",
centerIcon: Zap,
texts: ["React", "TypeScript", "Tailwind", "Node.js", "PostgreSQL", "Redis"],
},
{
bentoComponent: "media-stack",
title: "Media Gallery",
description: "Showcase your work with interactive media stacks.",
items: [
{ imageSrc: "https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=400&h=300&fit=crop" },
{ imageSrc: "https://images.unsplash.com/photo-1614850523459-c2f4c699c52e?w=400&h=300&fit=crop" },
{ imageSrc: "https://images.unsplash.com/photo-1620641788421-7a1c342ea42e?w=400&h=300&fit=crop" },
],
},
]}
/>
</>
);
export default FeaturesBentoPage;

View File

@@ -0,0 +1,43 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FeaturesComparison from "@/components/sections/features/FeaturesComparison";
const FeaturesComparisonPage = () => {
return (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FeaturesComparison
tag="Why Choose Us"
title="The Difference Is Clear"
description="See how we compare to traditional solutions"
primaryButton={{ text: "Get Started", href: "#" }}
secondaryButton={{ text: "Learn More", href: "#" }}
negativeItems={[
"Slow deployment times",
"Complex configuration",
"Limited scalability",
"No real-time updates",
"Poor documentation",
]}
positiveItems={[
"Instant deployments",
"Zero configuration",
"Infinite scalability",
"Real-time everything",
"Comprehensive docs",
]}
/>
</>
);
};
export default FeaturesComparisonPage;

View File

@@ -0,0 +1,49 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FeaturesDetailedCards from "@/components/sections/features/FeaturesDetailedCards";
const FeaturesDetailedCardsPage = () => {
return (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FeaturesDetailedCards
tag="Portfolio"
title="Featured Projects"
description="Explore our collection of successful projects crafted by talented creators"
primaryButton={{ text: "View All", href: "#" }}
secondaryButton={{ text: "Submit Project", href: "#" }}
items={[
{
title: "E-commerce Platform Redesign",
description: "A complete overhaul of the shopping experience with focus on mobile-first design and seamless checkout flow.",
tags: ["Web Design", "E-commerce", "UX Research"],
imageSrc: "/placeholders/placeholder.webp",
},
{
title: "Healthcare Dashboard",
description: "Real-time patient monitoring system with intuitive data visualization and predictive analytics.",
tags: ["Dashboard", "Healthcare", "Data Viz"],
imageSrc: "/placeholders/placeholder.webp",
},
{
title: "Fintech Mobile App",
description: "Modern banking application featuring biometric authentication and AI-powered financial insights.",
tags: ["Mobile", "FinTech", "AI"],
imageSrc: "/placeholders/placeholder.webp",
},
]}
/>
</>
);
};
export default FeaturesDetailedCardsPage;

View File

@@ -0,0 +1,52 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FeaturesDetailedSteps from "@/components/sections/features/FeaturesDetailedSteps";
const FeaturesDetailedStepsPage = () => {
return (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FeaturesDetailedSteps
tag="How It Works"
title="Your Journey to Success"
description="Follow these simple steps to transform your workflow"
primaryButton={{ text: "Get Started", href: "#" }}
secondaryButton={{ text: "Learn More", href: "#" }}
steps={[
{
tag: "Step One",
title: "Discovery",
subtitle: "Understanding Your Business Needs and Goals",
description: "We start by conducting in-depth research to understand your business objectives, target audience, and competitive landscape. This foundational phase ensures every decision we make is aligned with your vision.",
imageSrc: "/placeholders/placeholder.webp",
},
{
tag: "Step Two",
title: "Design",
subtitle: "Crafting a Tailored Strategic Solution",
description: "Our expert team translates insights into a comprehensive design strategy. We create detailed wireframes, prototypes, and visual concepts that bring your ideas to life while maintaining focus on user experience.",
imageSrc: "/placeholders/placeholder.webp",
},
{
tag: "Step Three",
title: "Deliver",
subtitle: "Bringing Your Vision to Reality",
description: "We implement the final solution with meticulous attention to detail and quality. Our team provides thorough testing, seamless deployment, and ongoing support to ensure long-term success and satisfaction.",
imageSrc: "/placeholders/placeholder.webp",
},
]}
/>
</>
);
};
export default FeaturesDetailedStepsPage;

View File

@@ -0,0 +1,59 @@
import { Zap, Shield, Rocket } from "lucide-react";
import NavbarCentered from "@/components/ui/NavbarCentered";
import FeaturesDualMedia from "@/components/sections/features/FeaturesDualMedia";
const FeaturesDualMediaPage = () => {
return (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FeaturesDualMedia
tag="Features"
title="Powerful Capabilities"
description="Discover the tools that will transform your workflow and boost productivity"
primaryButton={{ text: "Get Started", href: "#" }}
secondaryButton={{ text: "Learn More", href: "#" }}
items={[
{
icon: Zap,
title: "Lightning Fast Performance",
description: "Optimized for speed with intelligent caching and lazy loading for instant results.",
mediaItems: [
{ imageSrc: "/placeholders/placeholder.webp" },
{ imageSrc: "/placeholders/placeholder.webp" },
],
},
{
icon: Shield,
title: "Enterprise Security",
description: "Bank-grade encryption and compliance with industry security standards.",
mediaItems: [
{ imageSrc: "/placeholders/placeholder.webp" },
{ imageSrc: "/placeholders/placeholder.webp" },
],
},
{
icon: Rocket,
title: "Rapid Deployment",
description: "Go from development to production in minutes with automated pipelines.",
mediaItems: [
{ imageSrc: "/placeholders/placeholder.webp" },
{ imageSrc: "/placeholders/placeholder.webp" },
],
},
]}
/>
</>
);
};
export default FeaturesDualMediaPage;

View File

@@ -0,0 +1,67 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FeaturesFlipCards from "@/components/sections/features/FeaturesFlipCards";
const FeaturesFlipCardsPage = () => {
return (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FeaturesFlipCards
tag="How It Works"
title="Simple Process"
description="Click any card to learn more about each step in our streamlined workflow"
primaryButton={{ text: "Get Started", href: "#" }}
secondaryButton={{ text: "Learn More", href: "#" }}
items={[
{
title: "Discovery",
descriptions: [
"We start by understanding your goals, challenges, and vision.",
"Our team conducts thorough research to identify opportunities.",
"Together, we define the project scope and success metrics.",
],
imageSrc: "/placeholders/placeholder.webp",
},
{
title: "Design",
descriptions: [
"Our designers create wireframes and visual concepts.",
"We iterate based on your feedback until it's perfect.",
"Every detail is crafted with your users in mind.",
],
imageSrc: "/placeholders/placeholder.webp",
},
{
title: "Development",
descriptions: [
"Our engineers build with modern, scalable technologies.",
"Regular updates keep you informed throughout the process.",
"Quality assurance ensures a polished final product.",
],
imageSrc: "/placeholders/placeholder.webp",
},
{
title: "Launch",
descriptions: [
"We handle deployment and ensure everything runs smoothly.",
"Training and documentation empower your team.",
"Ongoing support keeps your product at its best.",
],
imageSrc: "/placeholders/placeholder.webp",
},
]}
/>
</>
);
};
export default FeaturesFlipCardsPage;

View File

@@ -0,0 +1,50 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FeaturesIconCards from "@/components/sections/features/FeaturesIconCards";
import { Zap, Shield, BarChart, Users } from "lucide-react";
const FeaturesIconCardsPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FeaturesIconCards
tag="Features"
title="Built for Modern Teams"
description="Powerful features designed to help your team collaborate and succeed together."
primaryButton={{ text: "Get Started", href: "#" }}
secondaryButton={{ text: "Learn More", href: "#" }}
features={[
{
icon: Zap,
title: "Lightning Fast",
description: "Optimized for speed with instant response times and seamless interactions.",
},
{
icon: Shield,
title: "Enterprise Security",
description: "Bank-level encryption and security protocols to keep your data safe.",
},
{
icon: BarChart,
title: "Advanced Analytics",
description: "Deep insights into your data with powerful visualization tools.",
},
{
icon: Users,
title: "Team Collaboration",
description: "Work together in real-time with your entire team across the globe.",
},
]}
/>
</>
);
export default FeaturesIconCardsPage;

View File

@@ -0,0 +1,51 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FeaturesLabeledList from "@/components/sections/features/FeaturesLabeledList";
const FeaturesLabeledListPage = () => {
return (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FeaturesLabeledList
tag="Features"
title="Everything You Need to Succeed"
description="Powerful features designed to help you build better products faster"
primaryButton={{ text: "Get Started", href: "#" }}
items={[
{
label: "01",
title: "Lightning Fast Performance",
bullets: ["Speed optimized", "CDN delivery", "Edge caching", "Instant deploys"],
primaryButton: { text: "Learn More", href: "#" },
secondaryButton: { text: "Documentation", href: "#" },
},
{
label: "02",
title: "Enterprise Security",
bullets: ["SOC 2 compliant", "End-to-end encryption", "SSO support", "Audit logs"],
primaryButton: { text: "Learn More", href: "#" },
secondaryButton: { text: "View Certifications", href: "#" },
},
{
label: "03",
title: "Developer Experience",
bullets: ["TypeScript first", "REST & GraphQL", "CLI tools", "SDK libraries"],
primaryButton: { text: "Get Started", href: "#" },
secondaryButton: { text: "API Reference", href: "#" },
},
]}
/>
</>
);
};
export default FeaturesLabeledListPage;

View File

@@ -0,0 +1,41 @@
import { Link } from "react-router-dom";
const featuresComponents = [
{ title: "Features Media Cards", href: "/components/sections/features/media-cards" },
{ title: "Features Alternating Split", href: "/components/sections/features/alternating-split" },
{ title: "Features Labeled List", href: "/components/sections/features/labeled-list" },
{ title: "Features Comparison", href: "/components/sections/features/comparison" },
{ title: "Features Detailed Steps", href: "/components/sections/features/detailed-steps" },
{ title: "Features Tagged Cards", href: "/components/sections/features/tagged-cards" },
{ title: "Features Arrow Cards", href: "/components/sections/features/arrow-cards" },
{ title: "Features Detailed Cards", href: "/components/sections/features/detailed-cards" },
{ title: "Features Dual Media", href: "/components/sections/features/dual-media" },
{ title: "Features Media Carousel", href: "/components/sections/features/media-carousel" },
{ title: "Features Flip Cards", href: "/components/sections/features/flip-cards" },
{ title: "Features Statistics Cards", href: "/components/sections/features/statistics-cards" },
{ title: "Features Reveal Cards", href: "/components/sections/features/reveal-cards" },
{ title: "Features Timeline Cards", href: "/components/sections/features/timeline-cards" },
{ title: "Features Icon Cards", href: "/components/sections/features/icon-cards" },
{ title: "Features Bento", href: "/components/sections/features/bento" },
{ title: "Features Profile Cards", href: "/components/sections/features/profile-cards" },
];
const FeaturesListPage = () => {
return (
<section className="min-h-screen py-16">
<div className="w-content-width mx-auto">
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
{featuresComponents.map((component) => (
<Link key={component.href} to={component.href}>
<div className="relative flex items-center justify-center aspect-square p-6 text-center card rounded-lg cursor-pointer hover:opacity-80 transition-opacity">
<h3 className="text-xl font-normal">{component.title}</h3>
</div>
</Link>
))}
</div>
</div>
</section>
);
};
export default FeaturesListPage;

View File

@@ -0,0 +1,34 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FeaturesMediaCards from "@/components/sections/features/FeaturesMediaCards";
const FeaturesMediaCardsPage = () => {
return (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FeaturesMediaCards
tag="Features"
title="Everything You Need to Succeed"
description="Powerful features designed to help you build better products faster"
primaryButton={{ text: "Get Started", href: "#" }}
items={[
{ title: "Lightning Fast", description: "Optimized for speed and performance", imageSrc: "/placeholders/placeholder.webp" },
{ title: "Secure by Default", description: "Built with security best practices", imageSrc: "/placeholders/placeholder.webp" },
{ title: "Easy Integration", description: "Works seamlessly with your stack", imageSrc: "/placeholders/placeholder.webp" },
{ title: "24/7 Support", description: "We're here whenever you need us", imageSrc: "/placeholders/placeholder.webp" },
]}
/>
</>
);
};
export default FeaturesMediaCardsPage;

View File

@@ -0,0 +1,60 @@
import { ArrowRight, Play, ExternalLink } from "lucide-react";
import NavbarCentered from "@/components/ui/NavbarCentered";
import FeaturesMediaCarousel from "@/components/sections/features/FeaturesMediaCarousel";
const FeaturesMediaCarouselPage = () => {
return (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FeaturesMediaCarousel
tag="Portfolio"
title="Featured Work"
description="Explore our latest projects and see how we bring ideas to life"
primaryButton={{ text: "View All Projects", href: "#" }}
secondaryButton={{ text: "Contact Us", href: "#" }}
items={[
{
title: "Brand Identity Design",
description: "Complete visual identity for a tech startup",
buttonIcon: ArrowRight,
buttonHref: "#",
imageSrc: "/placeholders/placeholder.webp",
},
{
title: "Product Launch Video",
description: "Cinematic promotional video for product launch",
buttonIcon: Play,
buttonHref: "#",
imageSrc: "/placeholders/placeholder.webp",
},
{
title: "E-commerce Platform",
description: "Full-stack development of online marketplace",
buttonIcon: ExternalLink,
buttonHref: "#",
imageSrc: "/placeholders/placeholder.webp",
},
{
title: "Mobile App Design",
description: "UI/UX design for fitness tracking application",
buttonIcon: ArrowRight,
buttonHref: "#",
imageSrc: "/placeholders/placeholder.webp",
},
]}
/>
</>
);
};
export default FeaturesMediaCarouselPage;

View File

@@ -0,0 +1,61 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FeaturesProfileCards from "@/components/sections/features/FeaturesProfileCards";
const FeaturesProfileCardsPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FeaturesProfileCards
tag="Team"
title="Meet Our Expert Team"
description="Talented individuals driving innovation and excellence in everything we do."
primaryButton={{ text: "Join Us", href: "#" }}
secondaryButton={{ text: "Learn More", href: "#" }}
items={[
{
title: "Sarah Chen",
description: "Leading our product vision with 10+ years of experience in building scalable solutions.",
avatarSrc: "https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=100&h=100&fit=crop",
imageSrc: "https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?w=600&h=700&fit=crop",
buttonText: "Connect",
buttonHref: "#",
},
{
title: "Marcus Johnson",
description: "Architecting robust systems that power millions of users worldwide.",
avatarSrc: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&h=100&fit=crop",
imageSrc: "https://images.unsplash.com/photo-1560250097-0b93528c311a?w=600&h=700&fit=crop",
buttonText: "Connect",
buttonHref: "#",
},
{
title: "Emily Rodriguez",
description: "Crafting beautiful interfaces that users love and remember.",
avatarSrc: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=100&h=100&fit=crop",
imageSrc: "https://images.unsplash.com/photo-1573497019940-1c28c88b4f3e?w=600&h=700&fit=crop",
buttonText: "Connect",
buttonHref: "#",
},
{
title: "David Park",
description: "Turning complex data into actionable insights for business growth.",
avatarSrc: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100&h=100&fit=crop",
imageSrc: "https://images.unsplash.com/photo-1519085360753-af0119f7cbe7?w=600&h=700&fit=crop",
buttonText: "Connect",
buttonHref: "#",
},
]}
/>
</>
);
export default FeaturesProfileCardsPage;

View File

@@ -0,0 +1,51 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FeaturesRevealCards from "@/components/sections/features/FeaturesRevealCards";
const FeaturesRevealCardsPage = () => {
return (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FeaturesRevealCards
tag="Our Process"
title="How We Work"
description="Hover over each card to learn more about our approach"
primaryButton={{ text: "Start Project", href: "#" }}
secondaryButton={{ text: "Learn More", href: "#" }}
items={[
{
title: "Discovery",
description: "We dive deep into understanding your goals, challenges, and vision to create a solid foundation.",
imageSrc: "/placeholders/placeholder.webp",
},
{
title: "Strategy",
description: "Based on insights gathered, we craft a tailored strategy that aligns with your objectives.",
imageSrc: "/placeholders/placeholder.webp",
},
{
title: "Design",
description: "Our team creates stunning visuals that bring your brand to life and engage your audience.",
imageSrc: "/placeholders/placeholder.webp",
},
{
title: "Deliver",
description: "We launch your project with precision and provide ongoing support for continued success.",
imageSrc: "/placeholders/placeholder.webp",
},
]}
/>
</>
);
};
export default FeaturesRevealCardsPage;

View File

@@ -0,0 +1,55 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FeaturesStatisticsCards from "@/components/sections/features/FeaturesStatisticsCards";
const FeaturesStatisticsCardsPage = () => {
return (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FeaturesStatisticsCards
tag="Performance"
title="Key Metrics"
description="Track the numbers that matter most to your business growth"
primaryButton={{ text: "View Dashboard", href: "#" }}
secondaryButton={{ text: "Learn More", href: "#" }}
items={[
{
title: "Revenue Growth",
description: "Year over year increase in total revenue",
label: "Finance",
value: "+127%",
},
{
title: "Customer Retention",
description: "Monthly active users who return",
label: "Engagement",
value: "94.2%",
},
{
title: "Response Time",
description: "Average support ticket resolution",
label: "Support",
value: "< 2hrs",
},
{
title: "Uptime",
description: "Platform availability this quarter",
label: "Infrastructure",
value: "99.9%",
},
]}
/>
</>
);
};
export default FeaturesStatisticsCardsPage;

View File

@@ -0,0 +1,59 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FeaturesTaggedCards from "@/components/sections/features/FeaturesTaggedCards";
const FeaturesTaggedCardsPage = () => {
return (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FeaturesTaggedCards
tag="Features"
title="Everything You Need"
description="Powerful tools designed to help you build faster and smarter"
primaryButton={{ text: "Get Started", href: "#" }}
secondaryButton={{ text: "Learn More", href: "#" }}
items={[
{
tag: "Analytics",
title: "Real-time Insights",
description: "Track performance metrics and user behavior with our comprehensive analytics dashboard.",
primaryButton: { text: "View Demo", href: "#" },
imageSrc: "/placeholders/placeholder.webp",
},
{
tag: "Security",
title: "Enterprise Security",
description: "Keep your data safe with end-to-end encryption and advanced security protocols.",
primaryButton: { text: "Learn More", href: "#" },
secondaryButton: { text: "Contact Sales", href: "#" },
imageSrc: "/placeholders/placeholder.webp",
},
{
tag: "Integration",
title: "Seamless Integrations",
description: "Connect with your favorite tools and services through our extensive API library.",
primaryButton: { text: "Explore", href: "#" },
imageSrc: "/placeholders/placeholder.webp",
},
{
tag: "Support",
title: "24/7 Support",
description: "Get help whenever you need it with our dedicated support team available around the clock.",
imageSrc: "/placeholders/placeholder.webp",
},
]}
/>
</>
);
};
export default FeaturesTaggedCardsPage;

View File

@@ -0,0 +1,49 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FeaturesTimelineCards from "@/components/sections/features/FeaturesTimelineCards";
const FeaturesTimelineCardsPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FeaturesTimelineCards
tag="Process"
title="How It Works"
description="Follow our simple four-step process to transform your workflow and achieve measurable results."
primaryButton={{ text: "Get Started", href: "#" }}
secondaryButton={{ text: "Learn More", href: "#" }}
items={[
{
title: "Discovery",
description: "We analyze your current workflow to identify opportunities for improvement and automation.",
imageSrc: "https://images.unsplash.com/photo-1552664730-d307ca884978?w=1200",
},
{
title: "Strategy",
description: "Our team develops a customized plan tailored to your specific needs and goals.",
imageSrc: "https://images.unsplash.com/photo-1454165804606-c3d57bc86b40?w=1200",
},
{
title: "Implementation",
description: "We execute the plan with precision, ensuring minimal disruption to your operations.",
imageSrc: "https://images.unsplash.com/photo-1531403009284-440f080d1e12?w=1200",
},
{
title: "Optimization",
description: "Continuous monitoring and refinement to maximize efficiency and deliver lasting results.",
imageSrc: "https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=1200",
},
]}
/>
</>
);
export default FeaturesTimelineCardsPage;

View File

@@ -0,0 +1,58 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FooterBasic from "@/components/sections/footer/FooterBasic";
const FooterBasicPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FooterBasic
columns={[
{
title: "Product",
items: [
{ label: "Features", href: "/components/sections/features" },
{ label: "Pricing", href: "/components/sections/pricing" },
{ label: "Metrics", href: "/components/sections/metrics" },
],
},
{
title: "Company",
items: [
{ label: "About", href: "/components/sections/about" },
{ label: "Blog", href: "/components/sections/blog" },
{ label: "Team", href: "/components/sections/team" },
],
},
{
title: "Support",
items: [
{ label: "FAQ", href: "/components/sections/faq" },
{ label: "Contact", href: "/components/sections/contact" },
{ label: "Footer", href: "/components/sections/footer" },
],
},
{
title: "Legal",
items: [
{ label: "Privacy", href: "#" },
{ label: "Terms", href: "#" },
{ label: "Cookies", href: "#" },
],
},
]}
leftText="© 2025 Webild. All rights reserved."
rightText="Made with care"
/>
</>
);
export default FooterBasicPage;

View File

@@ -0,0 +1,46 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FooterBrand from "@/components/sections/footer/FooterBrand";
const FooterBrandPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FooterBrand
brand="Webild"
columns={[
{
items: [
{ label: "Features", href: "/components/sections/features" },
{ label: "Pricing", href: "/components/sections/pricing" },
{ label: "Metrics", href: "/components/sections/metrics" },
],
},
{
items: [
{ label: "About", href: "/components/sections/about" },
{ label: "Blog", href: "/components/sections/blog" },
{ label: "Team", href: "/components/sections/team" },
],
},
{
items: [
{ label: "FAQ", href: "/components/sections/faq" },
{ label: "Contact", href: "/components/sections/contact" },
{ label: "Footer", href: "/components/sections/footer" },
],
},
]}
/>
</>
);
export default FooterBrandPage;

View File

@@ -0,0 +1,46 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FooterBrandReveal from "@/components/sections/footer/FooterBrandReveal";
const FooterBrandRevealPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[150vh] bg-background" />
<FooterBrandReveal
brand="Webild"
columns={[
{
items: [
{ label: "Features", href: "/components/sections/features" },
{ label: "Pricing", href: "/components/sections/pricing" },
{ label: "Metrics", href: "/components/sections/metrics" },
],
},
{
items: [
{ label: "About", href: "/components/sections/about" },
{ label: "Blog", href: "/components/sections/blog" },
{ label: "Team", href: "/components/sections/team" },
],
},
{
items: [
{ label: "FAQ", href: "/components/sections/faq" },
{ label: "Contact", href: "/components/sections/contact" },
{ label: "Footer", href: "/components/sections/footer" },
],
},
]}
/>
</>
);
export default FooterBrandRevealPage;

View File

@@ -0,0 +1,32 @@
import { Link } from "react-router-dom";
const footerComponents = [
{ title: "Footer Simple", href: "/components/sections/footer/simple" },
{ title: "Footer Simple Reveal", href: "/components/sections/footer/simple-reveal" },
{ title: "Footer Brand", href: "/components/sections/footer/brand" },
{ title: "Footer Brand Reveal", href: "/components/sections/footer/brand-reveal" },
{ title: "Footer Minimal", href: "/components/sections/footer/minimal" },
{ title: "Footer Simple Card", href: "/components/sections/footer/simple-card" },
{ title: "Footer Simple Media", href: "/components/sections/footer/simple-media" },
{ title: "Footer Basic", href: "/components/sections/footer/basic" },
];
const FooterListPage = () => {
return (
<section className="min-h-screen py-16">
<div className="w-content-width mx-auto">
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
{footerComponents.map((component) => (
<Link key={component.href} to={component.href}>
<div className="relative flex items-center justify-center aspect-square p-6 text-center card rounded-lg cursor-pointer hover:opacity-80 transition-opacity">
<h3 className="text-xl font-normal">{component.title}</h3>
</div>
</Link>
))}
</div>
</div>
</section>
);
};
export default FooterListPage;

View File

@@ -0,0 +1,30 @@
import { Mail, Globe, MessageCircle } from "lucide-react";
import NavbarCentered from "@/components/ui/NavbarCentered";
import FooterMinimal from "@/components/sections/footer/FooterMinimal";
const FooterMinimalPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FooterMinimal
brand="Webild"
copyright="© 2025 Webild. All rights reserved."
socialLinks={[
{ icon: Mail, href: "mailto:hello@webild.com" },
{ icon: Globe, href: "https://webild.com" },
{ icon: MessageCircle, href: "/components/sections/contact" },
]}
/>
</>
);
export default FooterMinimalPage;

View File

@@ -0,0 +1,54 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FooterSimpleCard from "@/components/sections/footer/FooterSimpleCard";
const FooterSimpleCardPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FooterSimpleCard
brand="Webild"
columns={[
{
title: "Product",
items: [
{ label: "Features", href: "/components/sections/features" },
{ label: "Pricing", href: "/components/sections/pricing" },
{ label: "Metrics", href: "/components/sections/metrics" },
],
},
{
title: "Company",
items: [
{ label: "About", href: "/components/sections/about" },
{ label: "Blog", href: "/components/sections/blog" },
{ label: "Team", href: "/components/sections/team" },
],
},
{
title: "Support",
items: [
{ label: "FAQ", href: "/components/sections/faq" },
{ label: "Contact", href: "/components/sections/contact" },
{ label: "Footer", href: "/components/sections/footer" },
],
},
]}
copyright="© 2025 Webild. All rights reserved."
links={[
{ label: "Privacy Policy", href: "#" },
{ label: "Terms of Service", href: "#" },
]}
/>
</>
);
export default FooterSimpleCardPage;

View File

@@ -0,0 +1,55 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FooterSimpleMedia from "@/components/sections/footer/FooterSimpleMedia";
const FooterSimpleMediaPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FooterSimpleMedia
imageSrc="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=1920"
brand="Webild"
columns={[
{
title: "Product",
items: [
{ label: "Features", href: "/components/sections/features" },
{ label: "Pricing", href: "/components/sections/pricing" },
{ label: "Metrics", href: "/components/sections/metrics" },
],
},
{
title: "Company",
items: [
{ label: "About", href: "/components/sections/about" },
{ label: "Blog", href: "/components/sections/blog" },
{ label: "Team", href: "/components/sections/team" },
],
},
{
title: "Support",
items: [
{ label: "FAQ", href: "/components/sections/faq" },
{ label: "Contact", href: "/components/sections/contact" },
{ label: "Footer", href: "/components/sections/footer" },
],
},
]}
copyright="© 2025 Webild. All rights reserved."
links={[
{ label: "Privacy Policy", href: "#" },
{ label: "Terms of Service", href: "#" },
]}
/>
</>
);
export default FooterSimpleMediaPage;

View File

@@ -0,0 +1,54 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FooterSimple from "@/components/sections/footer/FooterSimple";
const FooterSimplePage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[50vh]" />
<FooterSimple
brand="Webild"
columns={[
{
title: "Product",
items: [
{ label: "Features", href: "#" },
{ label: "Pricing", href: "#" },
{ label: "Documentation", href: "#" },
],
},
{
title: "Company",
items: [
{ label: "About", href: "#" },
{ label: "Blog", href: "#" },
{ label: "Careers", href: "#" },
],
},
{
title: "Support",
items: [
{ label: "Help Center", href: "#" },
{ label: "Contact", href: "#" },
{ label: "Status", href: "#" },
],
},
]}
copyright="© 2025 Webild. All rights reserved."
links={[
{ label: "Privacy Policy", href: "#" },
{ label: "Terms of Service", href: "#" },
]}
/>
</>
);
export default FooterSimplePage;

View File

@@ -0,0 +1,54 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import FooterSimpleReveal from "@/components/sections/footer/FooterSimpleReveal";
const FooterSimpleRevealPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-[150vh] bg-background" />
<FooterSimpleReveal
brand="Webild"
columns={[
{
title: "Product",
items: [
{ label: "Features", href: "#" },
{ label: "Pricing", href: "#" },
{ label: "Documentation", href: "#" },
],
},
{
title: "Company",
items: [
{ label: "About", href: "#" },
{ label: "Blog", href: "#" },
{ label: "Careers", href: "#" },
],
},
{
title: "Support",
items: [
{ label: "Help Center", href: "#" },
{ label: "Contact", href: "#" },
{ label: "Status", href: "#" },
],
},
]}
copyright="© 2025 Webild. All rights reserved."
links={[
{ label: "Privacy Policy", href: "#" },
{ label: "Terms of Service", href: "#" },
]}
/>
</>
);
export default FooterSimpleRevealPage;

View File

@@ -0,0 +1,38 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import HeroBillboard from "@/components/sections/hero/HeroBillboard";
const HeroBillboardPage = () => {
return (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Features", href: "#features" },
{ name: "Pricing", href: "#pricing" },
{ name: "About", href: "#about" },
{ name: "Contact", href: "#contact" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<HeroBillboard
tag="New Release"
title="Build Modern Web Experiences"
description="Create stunning, responsive websites with our comprehensive component library designed for modern development"
primaryButton={{ text: "Get Started", href: "#" }}
secondaryButton={{ text: "Learn More", href: "#about" }}
imageSrc="/placeholders/placeholder.webp"
/>
<div id="about" className="flex items-center justify-center h-screen bg-card">
<div className="text-center">
<h2 className="mb-4 text-4xl font-bold">About Section</h2>
<p className="text-lg text-foreground/60">
This section demonstrates the scroll-to functionality
</p>
</div>
</div>
</>
);
};
export default HeroBillboardPage;

View File

@@ -0,0 +1,44 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import HeroBillboardGallery from "@/components/sections/hero/HeroBillboardGallery";
const HeroGalleryPage = () => {
return (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Features", href: "#features" },
{ name: "Pricing", href: "#pricing" },
{ name: "About", href: "#about" },
{ name: "Contact", href: "#contact" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<HeroBillboardGallery
tag="Featured"
title="Explore Our Collection"
description="Discover amazing products and experiences through our curated gallery"
primaryButton={{ text: "View Gallery", href: "#" }}
secondaryButton={{ text: "Learn More", href: "#about" }}
items={[
{ imageSrc: "/placeholders/placeholder.webp" },
{ imageSrc: "/placeholders/placeholder.webp" },
{ imageSrc: "/placeholders/placeholder.webp" },
{ imageSrc: "/placeholders/placeholder.webp" },
{ imageSrc: "/placeholders/placeholder.webp" },
]}
/>
<div id="about" className="flex items-center justify-center h-screen bg-card">
<div className="text-center">
<h2 className="mb-4 text-4xl font-bold">About Section</h2>
<p className="text-lg text-foreground/60">
This section demonstrates the scroll-to functionality
</p>
</div>
</div>
</>
);
};
export default HeroGalleryPage;

View File

@@ -0,0 +1,27 @@
import { Link } from "react-router-dom";
const heroComponents = [
{ title: "Hero Split", href: "/components/sections/hero/split" },
{ title: "Hero Billboard", href: "/components/sections/hero/billboard" },
{ title: "Hero Billboard Gallery", href: "/components/sections/hero/gallery" },
];
const HeroListPage = () => {
return (
<section className="min-h-screen py-16">
<div className="w-content-width mx-auto">
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
{heroComponents.map((component) => (
<Link key={component.href} to={component.href}>
<div className="relative flex items-center justify-center aspect-square p-6 text-center card rounded-lg cursor-pointer hover:opacity-80 transition-opacity">
<h3 className="text-xl font-normal">{component.title}</h3>
</div>
</Link>
))}
</div>
</div>
</section>
);
};
export default HeroListPage;

View File

@@ -0,0 +1,39 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import HeroSplit from "@/components/sections/hero/HeroSplit";
const HeroSplitPage = () => {
return (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Features", href: "#features" },
{ name: "Pricing", href: "#pricing" },
{ name: "About", href: "#about" },
{ name: "Contact", href: "#contact" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<div className="h-screen" />
<HeroSplit
tag="New Release"
title="Build Modern Web Experiences"
description="Create stunning, responsive websites with our comprehensive component library designed for modern development"
primaryButton={{ text: "Get Started", href: "#" }}
secondaryButton={{ text: "Learn More", href: "#about" }}
imageSrc="/placeholders/placeholder.webp"
/>
<div id="about" className="flex items-center justify-center h-screen bg-card">
<div className="text-center">
<h2 className="mb-4 text-4xl font-bold">About Section</h2>
<p className="text-lg text-foreground/60">
This section demonstrates the scroll-to functionality
</p>
</div>
</div>
</>
);
};
export default HeroSplitPage;

View File

@@ -0,0 +1,25 @@
import { Link } from "react-router-dom";
const legalComponents = [
{ title: "Policy Content", href: "/components/sections/legal/policy-content" },
];
const LegalListPage = () => {
return (
<section className="min-h-screen py-16">
<div className="w-content-width mx-auto">
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
{legalComponents.map((component) => (
<Link key={component.href} to={component.href}>
<div className="relative flex items-center justify-center aspect-square p-6 text-center card rounded-lg cursor-pointer hover:opacity-80 transition-opacity">
<h3 className="text-xl font-normal">{component.title}</h3>
</div>
</Link>
))}
</div>
</div>
</section>
);
};
export default LegalListPage;

View File

@@ -0,0 +1,45 @@
import NavbarCentered from "@/components/ui/NavbarCentered";
import PolicyContent from "@/components/sections/legal/PolicyContent";
const PolicyContentPage = () => (
<>
<NavbarCentered
logo="Webild"
navItems={[
{ name: "Home", href: "/" },
{ name: "About", href: "#" },
{ name: "Services", href: "#" },
{ name: "Contact", href: "#" },
]}
ctaButton={{ text: "Get Started", href: "#" }}
/>
<PolicyContent
title="Privacy Policy"
subtitle="Last updated: January 1, 2025"
sections={[
{
heading: "Information We Collect",
content: [
{ type: "paragraph", text: "We collect information you provide directly to us, such as when you create an account, make a purchase, or contact us for support." },
{ type: "list", items: ["Name and email address", "Payment information", "Usage data and preferences"] },
],
},
{
heading: "How We Use Your Information",
content: [
{ type: "paragraph", text: "We use the information we collect to provide, maintain, and improve our services." },
{ type: "numbered-list", items: ["Process transactions and send related information", "Send promotional communications (with your consent)", "Respond to your comments and questions"] },
],
},
{
heading: "Data Security",
content: [
{ type: "paragraph", text: "We implement appropriate technical and organizational measures to protect your personal information against unauthorized access, alteration, disclosure, or destruction." },
],
},
]}
/>
</>
);
export default PolicyContentPage;

View File

@@ -0,0 +1,40 @@
import MetricsFeatureCards from "@/components/sections/metrics/MetricsFeatureCards";
const MetricsFeatureCardsPage = () => (
<MetricsFeatureCards
tag="Metrics"
title="What Sets Us Apart"
description="Measurable results backed by features that drive real business value."
metrics={[
{
value: "99.9%",
title: "Uptime Guarantee",
features: [
"Multi-region failover protection",
"Real-time monitoring and alerts",
"Automatic scaling under load",
],
},
{
value: "50ms",
title: "Average Response Time",
features: [
"Global CDN distribution",
"Edge caching optimization",
"Intelligent request routing",
],
},
{
value: "24/7",
title: "Support Coverage",
features: [
"Dedicated account managers",
"Priority ticket resolution",
"Direct engineering access",
],
},
]}
/>
);
export default MetricsFeatureCardsPage;

View File

@@ -0,0 +1,38 @@
import { Users, Zap, Globe, TrendingUp } from "lucide-react";
import MetricsGradientCards from "@/components/sections/metrics/MetricsGradientCards";
const MetricsGradientCardsPage = () => (
<MetricsGradientCards
tag="Metrics"
title="Numbers That Speak for Themselves"
description="Our platform has helped thousands of teams achieve measurable results and drive meaningful growth."
metrics={[
{
value: "10M+",
title: "Active Users",
description: "Professionals trust our platform for their daily workflows",
icon: Users,
},
{
value: "99.9%",
title: "Uptime",
description: "Enterprise-grade reliability you can count on",
icon: Zap,
},
{
value: "150+",
title: "Countries",
description: "Global reach with localized support and infrastructure",
icon: Globe,
},
{
value: "3x",
title: "Faster Growth",
description: "Average productivity increase reported by our customers",
icon: TrendingUp,
},
]}
/>
);
export default MetricsGradientCardsPage;

View File

@@ -0,0 +1,34 @@
import { Users, Clock, TrendingUp, Award } from "lucide-react";
import MetricsIconCards from "@/components/sections/metrics/MetricsIconCards";
const MetricsIconCardsPage = () => (
<MetricsIconCards
tag="Metrics"
title="Performance at a Glance"
description="Key metrics that showcase our commitment to excellence and continuous improvement."
metrics={[
{
icon: Users,
title: "Team Members",
value: "250+",
},
{
icon: Clock,
title: "Hours Saved",
value: "10K+",
},
{
icon: TrendingUp,
title: "Growth Rate",
value: "127%",
},
{
icon: Award,
title: "Awards Won",
value: "34",
},
]}
/>
);
export default MetricsIconCardsPage;

Some files were not shown because too many files have changed in this diff Show More