@vvedantb/ui 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -2
- package/package.json +11 -11
- package/src/icons/automation-icon.tsx +26 -0
- package/src/icons/bar-chart-icon.tsx +14 -0
- package/src/icons/board-icon.tsx +16 -0
- package/src/icons/bolt-icon.tsx +10 -0
- package/src/icons/building-icon.tsx +16 -0
- package/src/icons/checklist-icon.tsx +17 -0
- package/src/icons/document-icon.tsx +14 -0
- package/src/icons/draft-icon.tsx +18 -0
- package/src/icons/files-icon.tsx +17 -0
- package/src/icons/flask-icon.tsx +17 -0
- package/src/icons/inbox-arrow-icon.tsx +25 -0
- package/src/icons/inbox-icon.tsx +13 -0
- package/src/icons/index.ts +22 -0
- package/src/icons/memory-icon.tsx +20 -0
- package/src/icons/nav-icon-base.tsx +98 -0
- package/src/icons/review-icon.tsx +15 -0
- package/src/icons/search-icon.tsx +14 -0
- package/src/icons/settings-icon.tsx +13 -0
- package/src/icons/sidebar-toggle-icon.tsx +24 -0
- package/src/icons/terminal-icon.tsx +17 -0
- package/src/index.ts +3 -0
- package/tokens/src/css/index.css +2 -1
- package/tokens/src/css/nav-icons.css +755 -0
- package/app/README.md +0 -259
- package/tokens/README.md +0 -98
package/README.md
CHANGED
|
@@ -4,10 +4,10 @@ Shared React 19 web UI. Source-exported: no build step.
|
|
|
4
4
|
|
|
5
5
|
| Import | What it is |
|
|
6
6
|
| --- | --- |
|
|
7
|
-
| `@vvedantb/ui` | The only JavaScript entry. Primitives (Radix-based), AI elements, audio (`playChime`, `useReplyChime`, `playTone`), `toast` and `SonnerToaster`, the composer (`InputArea`, deprecated 0.3 `Composer`), shell (`SidebarHeader`, `PillTabs`, `SessionsHoverCard`, and the app shell: `AppShell`, `AppSidebar`, `SidebarRail`, `RailTile`, `SidebarPanel`, `SidebarAccountMenu`, `SignOutDialog` and related helpers), app helpers (confirm skip, update watcher, settings chrome, `useStableAuth`, and from 0.8.0 `createThemeProvider`, `createPageTitleContext`, `PageMotionProvider` and `formatCompactRelativeTime`), marketing pieces from 0.9.0 (`createLandingKit` and the `Landing*` parts, `SiteFooter`, `PricingModal`), `cn`, and the native tokens `tokens` and `Tokens`. See `app/README.md` in the repository for the app helpers. |
|
|
7
|
+
| `@vvedantb/ui` | The only JavaScript entry. Primitives (Radix-based), AI elements, audio (`playChime`, `useReplyChime`, `playTone`), `toast` and `SonnerToaster`, the composer (`InputArea`, deprecated 0.3 `Composer`), shell (`SidebarHeader`, `PillTabs`, `SessionsHoverCard`, and the app shell: `AppShell`, `AppSidebar`, `SidebarRail`, `RailTile`, `SidebarPanel`, `SidebarAccountMenu`, `SignOutDialog` and related helpers), app helpers (confirm skip, update watcher, settings chrome, `useStableAuth`, and from 0.8.0 `createThemeProvider`, `createPageTitleContext`, `PageMotionProvider` and `formatCompactRelativeTime`), marketing pieces from 0.9.0 (`createLandingKit` and the `Landing*` parts, `SiteFooter`, `PricingModal`), animated nav icons from 0.11.0 (`MemoryIcon`, `BoardIcon`, ... and `NavIconBase`), `cn`, and the native tokens `tokens` and `Tokens`. See `app/README.md` in the repository for the app helpers. |
|
|
8
8
|
| `@vvedantb/ui/tokens` | `tokens.css`: colour, radius, shadow, ease and font tokens for Tailwind v4. CSS. |
|
|
9
9
|
| `@vvedantb/ui/tokens/tokens.css` | Same file as `@vvedantb/ui/tokens`. CSS. |
|
|
10
|
-
| `@vvedantb/ui/tokens/css/*.css` | `index`, `base`, `dialog`, `modal`, `tabs`, `tabs-pills`, `utilities`, `scrollbar-thin`, `animations`, `compat-shadcn-rgb`. CSS. |
|
|
10
|
+
| `@vvedantb/ui/tokens/css/*.css` | `index`, `base`, `dialog`, `modal`, `tabs`, `tabs-pills`, `utilities`, `scrollbar-thin`, `animations`, `nav-icons`, `compat-shadcn-rgb`. CSS. |
|
|
11
11
|
| `@vvedantb/ui/tokens/compat/shadcn-rgb.css` | Same file as `tokens/css/compat-shadcn-rgb.css`. CSS. |
|
|
12
12
|
| `@vvedantb/ui/tokens/tailwind-preset` | Tailwind preset (`.mjs`) for `@config "@vvedantb/ui/tokens/tailwind-preset"`. Kept as a build-tool entry: Tailwind needs a resolvable package path, and it is not a React API. |
|
|
13
13
|
| `@vvedantb/ui/landing.css` | Landing page classes (`landing-atmosphere`, `landing-grain`, `landing-pulse-dot`, ambient graph and preview animations, smooth `#top` scrolling). CSS. |
|
|
@@ -880,6 +880,61 @@ shown in that colour mode), `wordmark`, `price`, `tagline`,
|
|
|
880
880
|
|
|
881
881
|
`RailCollapseTile({ collapsed, onToggle })`: the rail tile that shows or hides the sidebar panel ("Show sidebar" with the expand icon, or "Hide sidebar").
|
|
882
882
|
|
|
883
|
+
## Animated nav icons (0.11.0)
|
|
884
|
+
|
|
885
|
+
Eighteen 24x24 stroked icons that animate while their row or tile is hovered. Animation is plain CSS:
|
|
886
|
+
|
|
887
|
+
```css
|
|
888
|
+
@import "@vvedantb/ui/tokens/css/nav-icons.css"; /* or tokens/css/index.css, which includes it */
|
|
889
|
+
```
|
|
890
|
+
|
|
891
|
+
Hover needs an ancestor with the `group` class (rail tiles and nav rows have one):
|
|
892
|
+
|
|
893
|
+
```tsx
|
|
894
|
+
import { MemoryIcon, TerminalIcon } from "@vvedantb/ui";
|
|
895
|
+
|
|
896
|
+
<a className="group" href="/memory"><MemoryIcon /> Memory</a>
|
|
897
|
+
<button className="group"><TerminalIcon active={isCurrent} /></button>
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
| Icon | Shows | Stroke | Longest loop |
|
|
901
|
+
| --- | --- | --- | --- |
|
|
902
|
+
| `MemoryIcon` | Brain; the synapse fires | 1.7 | 1000 ms |
|
|
903
|
+
| `BoltIcon` | Lightning bolt; flashes | 1.7 | 900 ms |
|
|
904
|
+
| `FilesIcon` | Two stacked files; the back one slides | 1.7 | 1400 ms |
|
|
905
|
+
| `InboxIcon` | Tray; a letter drops in | 1.7 | 1300 ms |
|
|
906
|
+
| `BuildingIcon` | Building; windows blink | 1.7 | 1600 ms |
|
|
907
|
+
| `SettingsIcon` | Gear; spins | 1.7 | 2500 ms |
|
|
908
|
+
| `BoardIcon` | Three-column board; cards drop, one travels | 1.75 | 1800 ms |
|
|
909
|
+
| `ChecklistIcon` | Two checked tasks; checks redraw | 1.75 | 1600 ms |
|
|
910
|
+
| `TerminalIcon` | Terminal; prompt, typing, cursor. Has `active` | 1.75 | 1400 ms |
|
|
911
|
+
| `DocumentIcon` | Page; lines draw, corner folds | 1.75 | 1500 ms |
|
|
912
|
+
| `ReviewIcon` | Circle with a check | 1.75 | 1500 ms |
|
|
913
|
+
| `FlaskIcon` | Flask; sloshes, bubbles rise | 1.75 | 1400 ms |
|
|
914
|
+
| `InboxArrowIcon` | Tray with an arrow and a dot. Has `active` | 1.75 | 1100 ms |
|
|
915
|
+
| `DraftIcon` | Page with a pencil nib | 1.75 | 1500 ms |
|
|
916
|
+
| `AutomationIcon` | Orbit around a play button. Has `active` | 1.75 | 1800 ms |
|
|
917
|
+
| `BarChartIcon` | Bars and a trend line | 1.75 | 1600 ms |
|
|
918
|
+
| `SearchIcon` | Magnifying glass; scans | 1.75 | 1200 ms |
|
|
919
|
+
| `SidebarToggleIcon` | Window with a side panel. Has `collapsed` | 1.75 | 900 ms |
|
|
920
|
+
|
|
921
|
+
Props (type `IconProps`; all optional):
|
|
922
|
+
|
|
923
|
+
| Prop | Default | Behaviour |
|
|
924
|
+
| --- | --- | --- |
|
|
925
|
+
| `size` | `20` | `width` and `height`. Number or string. |
|
|
926
|
+
| `color` | `"currentColor"` | Any other value sets the svg's inline `color`; every part follows it. The default sets nothing, so `text-*` classes work. |
|
|
927
|
+
| `strokeWidth` | icon's own (table) | Shapes with their own stroke width (the bar chart trend line, the inbox arrow halo) keep it. |
|
|
928
|
+
| `className`, `style` | | Merged onto the svg (`vv-icon` class first). |
|
|
929
|
+
| `title` | | Renders `<title>`, `role="img"` and `aria-labelledby`. Without it the svg is `aria-hidden="true"`. |
|
|
930
|
+
| `animate` | `"hover"` | `"hover"`: animate while a `.group` ancestor is hovered. `true`: always. `false`: never, and no hover scale. Sets `data-animate`. |
|
|
931
|
+
| `duration` | | Length in ms of the icon's longest loop (table). Sets `--vv-icon-duration-scale` to `duration / longest loop`; every duration and delay in the icon is multiplied by it, so staggers keep their proportions. |
|
|
932
|
+
| `active` | `false` | `TerminalIcon`, `InboxArrowIcon`, `AutomationIcon`: solid variant with cut-out details. Other icons: `data-active="true"` only. |
|
|
933
|
+
| `cutoutColor` | sidebar accent | Cut-out tone while `active` (`CutoutIconProps`). Default `var(--color-sidebar-accent, var(--sidebar-accent))`, so the solid icon suits a `bg-sidebar-accent` tile. |
|
|
934
|
+
| `collapsed` | `false` | `SidebarToggleIcon` only: chevron points out (`SidebarToggleIconProps`). |
|
|
935
|
+
|
|
936
|
+
Other SVG props (`id`, `data-*`, handlers, `ref`) go to the svg. On group hover the svg scales to 1.15 (the first six icons, spring ease) or 1.12 (the rest). `NavIconBase` is the shared svg wrapper, for icons drawn in the same style. The stylesheet has no reduced-motion override.
|
|
937
|
+
|
|
883
938
|
## Toaster
|
|
884
939
|
|
|
885
940
|
```tsx
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vvedantb/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Shared React 19 UI for web apps: one JavaScript entry (primitives, shell pieces, app helpers including the Convex and Clerk provider, VITE env parsing and ensure-user, native tokens), design-token CSS, shared Tailwind v4 CSS and a Tailwind preset.",
|
|
6
6
|
"type": "module",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"./tokens/css/utilities.css": "./tokens/src/css/utilities.css",
|
|
29
29
|
"./tokens/css/scrollbar-thin.css": "./tokens/src/css/scrollbar-thin.css",
|
|
30
30
|
"./tokens/css/animations.css": "./tokens/src/css/animations.css",
|
|
31
|
+
"./tokens/css/nav-icons.css": "./tokens/src/css/nav-icons.css",
|
|
31
32
|
"./tokens/css/compat-shadcn-rgb.css": "./tokens/src/css/compat-shadcn-rgb.css",
|
|
32
33
|
"./tokens/compat/shadcn-rgb.css": "./tokens/src/css/compat-shadcn-rgb.css",
|
|
33
34
|
"./tokens/css/*.css": "./tokens/src/css/*.css",
|
|
@@ -54,15 +55,6 @@
|
|
|
54
55
|
"./src/ui/sonner.css",
|
|
55
56
|
"./src/marketing/landing.css"
|
|
56
57
|
],
|
|
57
|
-
"scripts": {
|
|
58
|
-
"check:tokens": "node tokens/scripts/check-component-tokens.mjs",
|
|
59
|
-
"build": "node tokens/scripts/check-exports.mjs && node tokens/scripts/check-component-tokens.mjs && tsc -p tsconfig.json",
|
|
60
|
-
"typecheck": "tsc -p tsconfig.json && tsc -p tokens/tsconfig.json",
|
|
61
|
-
"test": "node tokens/scripts/check-component-tokens.mjs && vitest run && node --test tokens/test/*.test.mjs app/tests-node/*.test.mjs",
|
|
62
|
-
"verify": "node tokens/scripts/check-component-tokens.mjs && node tokens/verify/run.mjs",
|
|
63
|
-
"analyze": "node tokens/verify/analyze.mjs",
|
|
64
|
-
"prepack": "node tokens/scripts/check-exports.mjs"
|
|
65
|
-
},
|
|
66
58
|
"peerDependencies": {
|
|
67
59
|
"@clerk/clerk-react": ">=5",
|
|
68
60
|
"@radix-ui/react-accordion": "^1.2.12",
|
|
@@ -181,5 +173,13 @@
|
|
|
181
173
|
"zod": {
|
|
182
174
|
"optional": true
|
|
183
175
|
}
|
|
176
|
+
},
|
|
177
|
+
"scripts": {
|
|
178
|
+
"check:tokens": "node tokens/scripts/check-component-tokens.mjs",
|
|
179
|
+
"build": "node tokens/scripts/check-exports.mjs && node tokens/scripts/check-component-tokens.mjs && tsc -p tsconfig.json",
|
|
180
|
+
"typecheck": "tsc -p tsconfig.json && tsc -p tokens/tsconfig.json",
|
|
181
|
+
"test": "node tokens/scripts/check-component-tokens.mjs && vitest run && node --test tokens/test/*.test.mjs app/tests-node/*.test.mjs",
|
|
182
|
+
"verify": "node tokens/scripts/check-component-tokens.mjs && node tokens/verify/run.mjs",
|
|
183
|
+
"analyze": "node tokens/verify/analyze.mjs"
|
|
184
184
|
}
|
|
185
|
-
}
|
|
185
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { NavIconBase, cutoutStyle, type CutoutIconProps } from "./nav-icon-base";
|
|
2
|
+
|
|
3
|
+
/** Dashed orbit around a play button; the orbit spins and sparks burst on hover. Solid while `active`. */
|
|
4
|
+
export function AutomationIcon({ strokeWidth = 1.75, cutoutColor, ...props }: CutoutIconProps) {
|
|
5
|
+
const { active } = props;
|
|
6
|
+
return (
|
|
7
|
+
<NavIconBase {...props} strokeWidth={strokeWidth} hoverScale="1.12" loop={1800}>
|
|
8
|
+
<circle cx="12" cy="12" r="9" strokeDasharray="2.5 3.5" className="vv-icon-automation-orbit" />
|
|
9
|
+
{/* Filling the dashed orbit itself reads as a cog; a disc inside it keeps the orbit's silhouette. */}
|
|
10
|
+
{active ? <circle cx="12" cy="12" r="6.5" fill="currentColor" stroke="none" /> : null}
|
|
11
|
+
<g className="vv-icon-automation-sparks">
|
|
12
|
+
<line x1="3.7" y1="3.7" x2="2.2" y2="2.2" />
|
|
13
|
+
<line x1="20.3" y1="3.7" x2="21.8" y2="2.2" />
|
|
14
|
+
<line x1="3.7" y1="20.3" x2="2.2" y2="21.8" />
|
|
15
|
+
<line x1="20.3" y1="20.3" x2="21.8" y2="21.8" />
|
|
16
|
+
</g>
|
|
17
|
+
<path
|
|
18
|
+
d="M10.4 8.6v6.8l5.6-3.4z"
|
|
19
|
+
fill="currentColor"
|
|
20
|
+
stroke="none"
|
|
21
|
+
className="vv-icon-automation-play"
|
|
22
|
+
style={cutoutStyle(active, "fill", cutoutColor)}
|
|
23
|
+
/>
|
|
24
|
+
</NavIconBase>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { NavIconBase, type IconProps } from "./nav-icon-base";
|
|
2
|
+
|
|
3
|
+
/** Three rising bars with a trend line; bars grow and the line draws on hover. */
|
|
4
|
+
export function BarChartIcon({ strokeWidth = 1.75, ...props }: IconProps) {
|
|
5
|
+
return (
|
|
6
|
+
<NavIconBase {...props} strokeWidth={strokeWidth} hoverScale="1.12" loop={1600}>
|
|
7
|
+
<line x1="3" y1="20" x2="21" y2="20" />
|
|
8
|
+
<rect x="5" y="14" width="3" height="6" rx="0.5" fill="currentColor" stroke="none" className="vv-icon-chart-bar vv-icon-chart-bar-1" />
|
|
9
|
+
<rect x="10.5" y="9" width="3" height="11" rx="0.5" fill="currentColor" stroke="none" className="vv-icon-chart-bar vv-icon-chart-bar-2" />
|
|
10
|
+
<rect x="16" y="5" width="3" height="15" rx="0.5" fill="currentColor" stroke="none" className="vv-icon-chart-bar vv-icon-chart-bar-3" />
|
|
11
|
+
<polyline points="6.5,14 12,9 17.5,5" strokeWidth="1.3" className="vv-icon-chart-trend" />
|
|
12
|
+
</NavIconBase>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { NavIconBase, type IconProps } from "./nav-icon-base";
|
|
2
|
+
|
|
3
|
+
/** Three-column board; cards drop in and a card travels across on hover. */
|
|
4
|
+
export function BoardIcon({ strokeWidth = 1.75, ...props }: IconProps) {
|
|
5
|
+
return (
|
|
6
|
+
<NavIconBase {...props} strokeWidth={strokeWidth} hoverScale="1.12" loop={1800}>
|
|
7
|
+
<rect x="3" y="3.5" width="5.5" height="17" rx="1.4" />
|
|
8
|
+
<rect x="9.25" y="3.5" width="5.5" height="11" rx="1.4" />
|
|
9
|
+
<rect x="15.5" y="3.5" width="5.5" height="14" rx="1.4" />
|
|
10
|
+
<rect x="4.4" y="5" width="2.7" height="2.2" rx="0.4" fill="currentColor" stroke="none" className="vv-icon-board-card vv-icon-board-card-1" />
|
|
11
|
+
<rect x="10.65" y="5" width="2.7" height="2.2" rx="0.4" fill="currentColor" stroke="none" className="vv-icon-board-card vv-icon-board-card-2" />
|
|
12
|
+
<rect x="16.9" y="5" width="2.7" height="2.2" rx="0.4" fill="currentColor" stroke="none" className="vv-icon-board-card vv-icon-board-card-3" />
|
|
13
|
+
<rect x="4.4" y="9" width="2.7" height="1.5" rx="0.4" fill="currentColor" stroke="none" className="vv-icon-board-traveler" />
|
|
14
|
+
</NavIconBase>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { NavIconBase, type IconProps } from "./nav-icon-base";
|
|
2
|
+
|
|
3
|
+
/** Lightning bolt that flashes on hover. */
|
|
4
|
+
export function BoltIcon({ strokeWidth = 1.7, ...props }: IconProps) {
|
|
5
|
+
return (
|
|
6
|
+
<NavIconBase {...props} strokeWidth={strokeWidth} hoverScale="1.15" loop={900}>
|
|
7
|
+
<path className="vv-icon-bolt" d="M13 3L4 14h7l-1 7l9 -11h-7l1 -7z" />
|
|
8
|
+
</NavIconBase>
|
|
9
|
+
);
|
|
10
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { NavIconBase, type IconProps } from "./nav-icon-base";
|
|
2
|
+
|
|
3
|
+
/** Building whose windows blink in turn on hover. */
|
|
4
|
+
export function BuildingIcon({ strokeWidth = 1.7, ...props }: IconProps) {
|
|
5
|
+
return (
|
|
6
|
+
<NavIconBase {...props} strokeWidth={strokeWidth} hoverScale="1.15" loop={1600}>
|
|
7
|
+
<path d="M4 20V6.5a1.5 1.5 0 0 1 1.5 -1.5h13a1.5 1.5 0 0 1 1.5 1.5V20" />
|
|
8
|
+
<path d="M3 20h18" />
|
|
9
|
+
<path d="M10 20v-4a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v4" />
|
|
10
|
+
<rect className="vv-icon-building-window vv-icon-building-window-1" x="7" y="8" width="3" height="3" rx="0.5" fill="currentColor" stroke="none" />
|
|
11
|
+
<rect className="vv-icon-building-window vv-icon-building-window-2" x="14" y="8" width="3" height="3" rx="0.5" fill="currentColor" stroke="none" />
|
|
12
|
+
<rect className="vv-icon-building-window vv-icon-building-window-3" x="7" y="12" width="3" height="2.5" rx="0.5" fill="currentColor" stroke="none" />
|
|
13
|
+
<rect className="vv-icon-building-window vv-icon-building-window-4" x="14" y="12" width="3" height="2.5" rx="0.5" fill="currentColor" stroke="none" />
|
|
14
|
+
</NavIconBase>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { NavIconBase, type IconProps } from "./nav-icon-base";
|
|
2
|
+
|
|
3
|
+
/** Two checkboxes with task lines; checks redraw and rings burst on hover. */
|
|
4
|
+
export function ChecklistIcon({ strokeWidth = 1.75, ...props }: IconProps) {
|
|
5
|
+
return (
|
|
6
|
+
<NavIconBase {...props} strokeWidth={strokeWidth} hoverScale="1.12" loop={1600}>
|
|
7
|
+
<rect x="3" y="4" width="6" height="6" rx="1.2" />
|
|
8
|
+
<rect x="3" y="14" width="6" height="6" rx="1.2" />
|
|
9
|
+
<rect x="3" y="4" width="6" height="6" rx="1.2" className="vv-icon-checklist-ring vv-icon-checklist-ring-1" />
|
|
10
|
+
<rect x="3" y="14" width="6" height="6" rx="1.2" className="vv-icon-checklist-ring vv-icon-checklist-ring-2" />
|
|
11
|
+
<line x1="11" y1="7" x2="20" y2="7" className="vv-icon-checklist-line vv-icon-checklist-line-1" />
|
|
12
|
+
<line x1="11" y1="17" x2="18" y2="17" className="vv-icon-checklist-line vv-icon-checklist-line-2" />
|
|
13
|
+
<path d="M4.4 7.1l1.5 1.5 2.4-2.6" className="vv-icon-checklist-check vv-icon-checklist-check-1" />
|
|
14
|
+
<path d="M4.4 17.1l1.5 1.5 2.4-2.6" className="vv-icon-checklist-check vv-icon-checklist-check-2" />
|
|
15
|
+
</NavIconBase>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { NavIconBase, type IconProps } from "./nav-icon-base";
|
|
2
|
+
|
|
3
|
+
/** Page with a folded corner; lines draw on hover. */
|
|
4
|
+
export function DocumentIcon({ strokeWidth = 1.75, ...props }: IconProps) {
|
|
5
|
+
return (
|
|
6
|
+
<NavIconBase {...props} strokeWidth={strokeWidth} hoverScale="1.12" loop={1500}>
|
|
7
|
+
<path d="M14 3H7a1.5 1.5 0 0 0-1.5 1.5v15A1.5 1.5 0 0 0 7 21h11a1.5 1.5 0 0 0 1.5-1.5V8.5z" />
|
|
8
|
+
<path d="M14 3v4.5a1 1 0 0 0 1 1h4.5" className="vv-icon-document-corner" />
|
|
9
|
+
<line x1="9" y1="12" x2="16" y2="12" className="vv-icon-document-line vv-icon-document-line-1" />
|
|
10
|
+
<line x1="9" y1="15" x2="16" y2="15" className="vv-icon-document-line vv-icon-document-line-2" />
|
|
11
|
+
<line x1="9" y1="18" x2="13" y2="18" className="vv-icon-document-line vv-icon-document-line-3" />
|
|
12
|
+
</NavIconBase>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { NavIconBase, type IconProps } from "./nav-icon-base";
|
|
2
|
+
|
|
3
|
+
/** Page with a dashed line and a pencil nib. Shares the document animation. */
|
|
4
|
+
export function DraftIcon({ strokeWidth = 1.75, ...props }: IconProps) {
|
|
5
|
+
return (
|
|
6
|
+
<NavIconBase {...props} strokeWidth={strokeWidth} hoverScale="1.12" loop={1500}>
|
|
7
|
+
<path d="M14 3H7a1.5 1.5 0 0 0-1.5 1.5v15A1.5 1.5 0 0 0 7 21h11a1.5 1.5 0 0 0 1.5-1.5V8.5z" />
|
|
8
|
+
<path d="M14 3v4.5a1 1 0 0 0 1 1h4.5" className="vv-icon-document-corner" />
|
|
9
|
+
<line x1="9" y1="12" x2="16" y2="12" className="vv-icon-document-line vv-icon-document-line-1" />
|
|
10
|
+
<line x1="9" y1="15" x2="13" y2="15" className="vv-icon-document-line vv-icon-document-line-2" strokeDasharray="1.5 1.5" />
|
|
11
|
+
{/* Pencil nib (not animated) */}
|
|
12
|
+
<g className="vv-icon-draft-pencil">
|
|
13
|
+
<path d="M16.5 16.5l1.5-1.5 1.2 1.2-1.5 1.5z" fill="currentColor" stroke="none" />
|
|
14
|
+
<path d="M16.5 16.5l-0.8 2.3 2.3-0.8z" fill="currentColor" stroke="none" />
|
|
15
|
+
</g>
|
|
16
|
+
</NavIconBase>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { NavIconBase, type IconProps } from "./nav-icon-base";
|
|
2
|
+
|
|
3
|
+
/** Two stacked files; the back one slides out on hover. */
|
|
4
|
+
export function FilesIcon({ strokeWidth = 1.7, ...props }: IconProps) {
|
|
5
|
+
return (
|
|
6
|
+
<NavIconBase {...props} strokeWidth={strokeWidth} hoverScale="1.15" loop={1400}>
|
|
7
|
+
<g className="vv-icon-files-back">
|
|
8
|
+
<path d="M10 4h6l3 3v9a1.5 1.5 0 0 1 -1.5 1.5h-7.5a1.5 1.5 0 0 1 -1.5 -1.5v-10.5a1.5 1.5 0 0 1 1.5 -1.5z" />
|
|
9
|
+
<path d="M16 4v3h3" />
|
|
10
|
+
</g>
|
|
11
|
+
<g>
|
|
12
|
+
<path d="M6.5 8h6l3 3v9a1.5 1.5 0 0 1 -1.5 1.5h-7.5a1.5 1.5 0 0 1 -1.5 -1.5v-10.5a1.5 1.5 0 0 1 1.5 -1.5z" />
|
|
13
|
+
<path d="M12.5 8v3h3" />
|
|
14
|
+
</g>
|
|
15
|
+
</NavIconBase>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { NavIconBase, type IconProps } from "./nav-icon-base";
|
|
2
|
+
|
|
3
|
+
/** Flask that sloshes while bubbles rise on hover. */
|
|
4
|
+
export function FlaskIcon({ strokeWidth = 1.75, ...props }: IconProps) {
|
|
5
|
+
return (
|
|
6
|
+
<NavIconBase {...props} strokeWidth={strokeWidth} hoverScale="1.12" loop={1400}>
|
|
7
|
+
<g className="vv-icon-flask">
|
|
8
|
+
<path d="M9 3h6" />
|
|
9
|
+
<path d="M10 3v6.4L5.4 18A2 2 0 0 0 7.2 21h9.6a2 2 0 0 0 1.8-3l-4.6-8.6V3" />
|
|
10
|
+
</g>
|
|
11
|
+
<circle cx="11" cy="17.5" r="0.7" fill="currentColor" stroke="none" className="vv-icon-flask-bubble vv-icon-flask-bubble-1" />
|
|
12
|
+
<circle cx="13.5" cy="15.5" r="0.55" fill="currentColor" stroke="none" className="vv-icon-flask-bubble vv-icon-flask-bubble-2" />
|
|
13
|
+
<circle cx="12.4" cy="19" r="0.45" fill="currentColor" stroke="none" className="vv-icon-flask-bubble vv-icon-flask-bubble-3" />
|
|
14
|
+
<circle cx="14.6" cy="18" r="0.4" fill="currentColor" stroke="none" className="vv-icon-flask-bubble vv-icon-flask-bubble-4" />
|
|
15
|
+
</NavIconBase>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { NavIconBase, cutoutStyle, type CutoutIconProps } from "./nav-icon-base";
|
|
2
|
+
|
|
3
|
+
/** Tray with an arrow dropping in and a notification dot. Solid while `active`. */
|
|
4
|
+
export function InboxArrowIcon({ strokeWidth = 1.75, cutoutColor, ...props }: CutoutIconProps) {
|
|
5
|
+
const { active } = props;
|
|
6
|
+
const cutoutStroke = cutoutStyle(active, "stroke", cutoutColor);
|
|
7
|
+
return (
|
|
8
|
+
<NavIconBase {...props} strokeWidth={strokeWidth} hoverScale="1.12" loop={1100}>
|
|
9
|
+
<path d="M3 13.5l3-7.5h12l3 7.5v5.5A2 2 0 0 1 19 21H5a2 2 0 0 1-2-2z" fill={active ? "currentColor" : "none"} />
|
|
10
|
+
<path d="M3 13.5h5.2l1 2h5.6l1-2H21" style={cutoutStroke} />
|
|
11
|
+
<g className="vv-icon-inbox-arrow">
|
|
12
|
+
{/* The arrow dips into the tray; a cut-out halo keeps it legible against the solid body. */}
|
|
13
|
+
{active ? (
|
|
14
|
+
<g strokeWidth={4} style={cutoutStroke}>
|
|
15
|
+
<line x1="12" y1="2.5" x2="12" y2="8" />
|
|
16
|
+
<polyline points="9.6,5.6 12,8 14.4,5.6" />
|
|
17
|
+
</g>
|
|
18
|
+
) : null}
|
|
19
|
+
<line x1="12" y1="2.5" x2="12" y2="8" />
|
|
20
|
+
<polyline points="9.6,5.6 12,8 14.4,5.6" />
|
|
21
|
+
</g>
|
|
22
|
+
<circle cx="18.5" cy="4" r="1.4" fill="currentColor" stroke="none" className="vv-icon-inbox-notif" />
|
|
23
|
+
</NavIconBase>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { NavIconBase, type IconProps } from "./nav-icon-base";
|
|
2
|
+
|
|
3
|
+
/** Tray with a letter dropping in on hover. */
|
|
4
|
+
export function InboxIcon({ strokeWidth = 1.7, ...props }: IconProps) {
|
|
5
|
+
return (
|
|
6
|
+
<NavIconBase {...props} strokeWidth={strokeWidth} hoverScale="1.15" loop={1300}>
|
|
7
|
+
<rect className="vv-icon-inbox-letter" x="9" y="8" width="6" height="4" rx="0.5" fill="currentColor" stroke="none" />
|
|
8
|
+
<path d="M4 13l2 -8a1 1 0 0 1 1 -1h10a1 1 0 0 1 1 1l2 8" />
|
|
9
|
+
<path d="M4 13v6a1 1 0 0 0 1 1h14a1 1 0 0 0 1 -1v-6" />
|
|
10
|
+
<path d="M4 13h4l1 2h6l1 -2h4" />
|
|
11
|
+
</NavIconBase>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Animated nav icons. Styles: `@vvedantb/ui/tokens/css/nav-icons.css` (also in `tokens/css/index.css`).
|
|
2
|
+
export { NavIconBase } from "./nav-icon-base";
|
|
3
|
+
export type { CutoutIconProps, IconAnimate, IconProps, NavIconBaseProps } from "./nav-icon-base";
|
|
4
|
+
export { MemoryIcon } from "./memory-icon";
|
|
5
|
+
export { BoltIcon } from "./bolt-icon";
|
|
6
|
+
export { FilesIcon } from "./files-icon";
|
|
7
|
+
export { InboxIcon } from "./inbox-icon";
|
|
8
|
+
export { BuildingIcon } from "./building-icon";
|
|
9
|
+
export { SettingsIcon } from "./settings-icon";
|
|
10
|
+
export { BoardIcon } from "./board-icon";
|
|
11
|
+
export { ChecklistIcon } from "./checklist-icon";
|
|
12
|
+
export { TerminalIcon } from "./terminal-icon";
|
|
13
|
+
export { DocumentIcon } from "./document-icon";
|
|
14
|
+
export { ReviewIcon } from "./review-icon";
|
|
15
|
+
export { FlaskIcon } from "./flask-icon";
|
|
16
|
+
export { InboxArrowIcon } from "./inbox-arrow-icon";
|
|
17
|
+
export { DraftIcon } from "./draft-icon";
|
|
18
|
+
export { AutomationIcon } from "./automation-icon";
|
|
19
|
+
export { BarChartIcon } from "./bar-chart-icon";
|
|
20
|
+
export { SearchIcon } from "./search-icon";
|
|
21
|
+
export { SidebarToggleIcon } from "./sidebar-toggle-icon";
|
|
22
|
+
export type { SidebarToggleIconProps } from "./sidebar-toggle-icon";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { NavIconBase, type IconProps } from "./nav-icon-base";
|
|
2
|
+
|
|
3
|
+
/** Brain with a synapse that fires on hover. */
|
|
4
|
+
export function MemoryIcon({ strokeWidth = 1.7, ...props }: IconProps) {
|
|
5
|
+
return (
|
|
6
|
+
<NavIconBase {...props} strokeWidth={strokeWidth} hoverScale="1.15" loop={1000}>
|
|
7
|
+
{/* brain silhouette (symmetric about x=12) */}
|
|
8
|
+
<path
|
|
9
|
+
d="M12 3.5C8.5 2.2 5.5 3.3 5 6.5C2.8 7 2.5 8.5 3.5 10C2.2 11.5 2.5 13.5 4.5 14.5C4.5 17 6 18.5 8 18.5C9.5 20 11 19.6 12 19C13 19.6 14.5 20 16 18.5C18 18.5 19.5 17 19.5 14.5C21.5 13.5 21.8 11.5 20.5 10C21.5 8.5 21.2 7 19 6.5C18.5 3.3 15.5 2.2 12 3.5Z"
|
|
10
|
+
fill="none"
|
|
11
|
+
/>
|
|
12
|
+
{/* central fissure between the hemispheres */}
|
|
13
|
+
<path d="M12 3.9C10.6 6.5 13.4 8.8 12 11.4" fill="none" />
|
|
14
|
+
{/* synapse ping ring */}
|
|
15
|
+
<circle className="vv-icon-memory-ring" cx="12" cy="13.8" r="2.2" fill="none" />
|
|
16
|
+
{/* synapse core */}
|
|
17
|
+
<circle className="vv-icon-memory-core" cx="12" cy="13.8" r="1.7" fill="currentColor" stroke="none" />
|
|
18
|
+
</NavIconBase>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { useId, type CSSProperties, type ReactNode, type SVGProps } from "react";
|
|
2
|
+
import { cn } from "../utils/cn";
|
|
3
|
+
|
|
4
|
+
/** `"hover"`: animate while an ancestor with the `group` class is hovered. `true`: always. `false`: never. */
|
|
5
|
+
export type IconAnimate = boolean | "hover";
|
|
6
|
+
|
|
7
|
+
export type IconProps = Omit<SVGProps<SVGSVGElement>, "children" | "color" | "height" | "width" | "strokeWidth"> & {
|
|
8
|
+
/** Width and height. Default 20. */
|
|
9
|
+
size?: number | string;
|
|
10
|
+
/** Sets the svg's CSS `color`, which every `currentColor` part follows. Default `currentColor` (no inline colour). */
|
|
11
|
+
color?: string;
|
|
12
|
+
/** Default: the icon's own stroke (1.7 or 1.75). Shapes with their own stroke width keep it. */
|
|
13
|
+
strokeWidth?: number | string;
|
|
14
|
+
/** Accessible name. Without it the icon is `aria-hidden`. */
|
|
15
|
+
title?: string;
|
|
16
|
+
/** Default `"hover"`. */
|
|
17
|
+
animate?: IconAnimate;
|
|
18
|
+
/** Solid variant on icons that have one; elsewhere only sets `data-active`. */
|
|
19
|
+
active?: boolean;
|
|
20
|
+
/** Length in ms of the icon's longest loop. Every other duration and delay in the icon scales with it. */
|
|
21
|
+
duration?: number;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/** Icons with a solid `active` variant. Inner details are painted in `cutoutColor`. */
|
|
25
|
+
export type CutoutIconProps = IconProps & {
|
|
26
|
+
/** Tone of the cut-out details while `active`. Default: the sidebar accent colour. */
|
|
27
|
+
cutoutColor?: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type NavIconBaseProps = IconProps & {
|
|
31
|
+
children: ReactNode;
|
|
32
|
+
/** Root scale on group hover: `"1.15"` springs, `"1.12"` eases out. */
|
|
33
|
+
hoverScale: "1.15" | "1.12";
|
|
34
|
+
/** The icon's longest loop in ms, which `duration` replaces. */
|
|
35
|
+
loop: number;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
type IconStyle = CSSProperties & { "--vv-icon-duration-scale"?: number };
|
|
39
|
+
|
|
40
|
+
const CUTOUT_TONE = "var(--color-sidebar-accent, var(--sidebar-accent))";
|
|
41
|
+
|
|
42
|
+
/** Inline stroke or fill for a cut-out detail while active, so it reads as a hole in the solid shape. */
|
|
43
|
+
export function cutoutStyle(
|
|
44
|
+
active: boolean | undefined,
|
|
45
|
+
kind: "stroke" | "fill",
|
|
46
|
+
cutoutColor: string | undefined,
|
|
47
|
+
): CSSProperties | undefined {
|
|
48
|
+
if (!active) return undefined;
|
|
49
|
+
const tone = cutoutColor ?? CUTOUT_TONE;
|
|
50
|
+
return kind === "stroke" ? { stroke: tone } : { fill: tone };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** The 24x24 stroked svg every nav icon renders. Animation lives in `tokens/css/nav-icons.css`. */
|
|
54
|
+
export function NavIconBase({
|
|
55
|
+
size = 20,
|
|
56
|
+
color,
|
|
57
|
+
strokeWidth,
|
|
58
|
+
className,
|
|
59
|
+
style,
|
|
60
|
+
title,
|
|
61
|
+
animate = "hover",
|
|
62
|
+
active,
|
|
63
|
+
duration,
|
|
64
|
+
hoverScale,
|
|
65
|
+
loop,
|
|
66
|
+
children,
|
|
67
|
+
...rest
|
|
68
|
+
}: NavIconBaseProps) {
|
|
69
|
+
const titleId = useId();
|
|
70
|
+
const iconStyle: IconStyle = { ...style };
|
|
71
|
+
if (color !== undefined && color !== "currentColor") iconStyle.color = color;
|
|
72
|
+
if (duration !== undefined && duration > 0) iconStyle["--vv-icon-duration-scale"] = duration / loop;
|
|
73
|
+
return (
|
|
74
|
+
<svg
|
|
75
|
+
{...rest}
|
|
76
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
77
|
+
width={size}
|
|
78
|
+
height={size}
|
|
79
|
+
viewBox="0 0 24 24"
|
|
80
|
+
fill="none"
|
|
81
|
+
stroke="currentColor"
|
|
82
|
+
strokeWidth={strokeWidth}
|
|
83
|
+
strokeLinecap="round"
|
|
84
|
+
strokeLinejoin="round"
|
|
85
|
+
className={cn("vv-icon", className)}
|
|
86
|
+
style={Object.keys(iconStyle).length > 0 ? iconStyle : undefined}
|
|
87
|
+
data-animate={String(animate)}
|
|
88
|
+
data-hover-scale={hoverScale}
|
|
89
|
+
data-active={active ? "true" : undefined}
|
|
90
|
+
role={title ? "img" : undefined}
|
|
91
|
+
aria-hidden={title ? undefined : true}
|
|
92
|
+
aria-labelledby={title ? titleId : undefined}
|
|
93
|
+
>
|
|
94
|
+
{title ? <title id={titleId}>{title}</title> : null}
|
|
95
|
+
{children}
|
|
96
|
+
</svg>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { NavIconBase, type IconProps } from "./nav-icon-base";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Circle with a check and a line. The marks take the document line animation
|
|
5
|
+
* without `vv-icon-document-line`, so they scale from the svg origin, not their own box.
|
|
6
|
+
*/
|
|
7
|
+
export function ReviewIcon({ strokeWidth = 1.75, ...props }: IconProps) {
|
|
8
|
+
return (
|
|
9
|
+
<NavIconBase {...props} strokeWidth={strokeWidth} hoverScale="1.12" loop={1500}>
|
|
10
|
+
<circle cx="12" cy="12" r="9" />
|
|
11
|
+
<path d="M9.5 12.5 11.2 14.2 14.8 10" className="vv-icon-document-line-1" />
|
|
12
|
+
<path d="M8 7.5h8" className="vv-icon-document-line-2" />
|
|
13
|
+
</NavIconBase>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { NavIconBase, type IconProps } from "./nav-icon-base";
|
|
2
|
+
|
|
3
|
+
/** Magnifying glass that scans with a lens glint on hover. */
|
|
4
|
+
export function SearchIcon({ strokeWidth = 1.75, ...props }: IconProps) {
|
|
5
|
+
return (
|
|
6
|
+
<NavIconBase {...props} strokeWidth={strokeWidth} hoverScale="1.12" loop={1200}>
|
|
7
|
+
<g className="vv-icon-search">
|
|
8
|
+
<circle cx="10.5" cy="10.5" r="6.5" />
|
|
9
|
+
<line x1="15.2" y1="15.2" x2="20.5" y2="20.5" />
|
|
10
|
+
<circle cx="8.6" cy="8.6" r="1.1" fill="currentColor" stroke="none" className="vv-icon-search-glint" />
|
|
11
|
+
</g>
|
|
12
|
+
</NavIconBase>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { NavIconBase, type IconProps } from "./nav-icon-base";
|
|
2
|
+
|
|
3
|
+
/** Gear that spins on hover. */
|
|
4
|
+
export function SettingsIcon({ strokeWidth = 1.7, ...props }: IconProps) {
|
|
5
|
+
return (
|
|
6
|
+
<NavIconBase {...props} strokeWidth={strokeWidth} hoverScale="1.15" loop={2500}>
|
|
7
|
+
<g className="vv-icon-gear">
|
|
8
|
+
<path d="M10.325 4.317c.426 -1.756 2.924 -1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543 -.94 3.31 .826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756 .426 1.756 2.924 0 3.35a1.724 1.724 0 0 0 -1.066 2.573c.94 1.543 -.826 3.31 -2.37 2.37a1.724 1.724 0 0 0 -2.572 1.065c-.426 1.756 -2.924 1.756 -3.35 0a1.724 1.724 0 0 0 -2.573 -1.066c-1.543 .94 -3.31 -.826 -2.37 -2.37a1.724 1.724 0 0 0 -1.065 -2.572c-1.756 -.426 -1.756 -2.924 0 -3.35a1.724 1.724 0 0 0 1.066 -2.573c-.94 -1.543 .826 -3.31 2.37 -2.37c1 .608 2.296 .07 2.572 -1.065z" />
|
|
9
|
+
<circle cx="12" cy="12" r="3" />
|
|
10
|
+
</g>
|
|
11
|
+
</NavIconBase>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { cn } from "../utils/cn";
|
|
2
|
+
import { NavIconBase, type IconProps } from "./nav-icon-base";
|
|
3
|
+
|
|
4
|
+
export type SidebarToggleIconProps = IconProps & {
|
|
5
|
+
/** Chevron points out (expand) instead of in (collapse). */
|
|
6
|
+
collapsed?: boolean;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/** Window with a side panel; the panel squeezes and the chevron nudges on hover. */
|
|
10
|
+
export function SidebarToggleIcon({ strokeWidth = 1.75, collapsed = false, className, ...props }: SidebarToggleIconProps) {
|
|
11
|
+
return (
|
|
12
|
+
<NavIconBase
|
|
13
|
+
{...props}
|
|
14
|
+
strokeWidth={strokeWidth}
|
|
15
|
+
className={cn(collapsed && "vv-icon-sidebar-collapsed", className)}
|
|
16
|
+
hoverScale="1.12"
|
|
17
|
+
loop={900}
|
|
18
|
+
>
|
|
19
|
+
<rect x="3" y="4" width="18" height="16" rx="2" />
|
|
20
|
+
<rect x="3.5" y="4.5" width="5.5" height="15" rx="1.2" fill="currentColor" stroke="none" className="vv-icon-sidebar-panel" />
|
|
21
|
+
<path d={collapsed ? "M13 9l3 3-3 3" : "M16 9l-3 3 3 3"} className="vv-icon-sidebar-chevron" />
|
|
22
|
+
</NavIconBase>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { NavIconBase, cutoutStyle, type CutoutIconProps } from "./nav-icon-base";
|
|
2
|
+
|
|
3
|
+
/** Terminal window with a prompt, typed characters and a blinking cursor. Solid while `active`. */
|
|
4
|
+
export function TerminalIcon({ strokeWidth = 1.75, cutoutColor, ...props }: CutoutIconProps) {
|
|
5
|
+
const { active } = props;
|
|
6
|
+
const cutoutFill = cutoutStyle(active, "fill", cutoutColor);
|
|
7
|
+
return (
|
|
8
|
+
<NavIconBase {...props} strokeWidth={strokeWidth} hoverScale="1.12" loop={1400}>
|
|
9
|
+
<rect x="3" y="4" width="18" height="16" rx="2" fill={active ? "currentColor" : "none"} />
|
|
10
|
+
<path d="M7 10l3 2-3 2" className="vv-icon-terminal-prompt" style={cutoutStyle(active, "stroke", cutoutColor)} />
|
|
11
|
+
<rect x="13" y="13.5" width="1" height="1" rx="0.2" fill="currentColor" stroke="none" className="vv-icon-terminal-typed vv-icon-terminal-typed-1" style={cutoutFill} />
|
|
12
|
+
<rect x="14.6" y="13.5" width="1" height="1" rx="0.2" fill="currentColor" stroke="none" className="vv-icon-terminal-typed vv-icon-terminal-typed-2" style={cutoutFill} />
|
|
13
|
+
<rect x="16.2" y="13.5" width="1" height="1" rx="0.2" fill="currentColor" stroke="none" className="vv-icon-terminal-typed vv-icon-terminal-typed-3" style={cutoutFill} />
|
|
14
|
+
<rect x="17.8" y="13.2" width="1.4" height="1.6" rx="0.3" fill="currentColor" stroke="none" className="vv-icon-terminal-cursor" style={cutoutFill} />
|
|
15
|
+
</NavIconBase>
|
|
16
|
+
);
|
|
17
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -61,6 +61,9 @@ export * from "./ui/sheet";
|
|
|
61
61
|
export * from "./ui/visually-hidden";
|
|
62
62
|
export * from "./ui/clearInputDissolve";
|
|
63
63
|
|
|
64
|
+
// Animated nav icons. Styles: `@vvedantb/ui/tokens/css/nav-icons.css`.
|
|
65
|
+
export * from "./icons";
|
|
66
|
+
|
|
64
67
|
// Dual copies: the richer file uses the plain name; the other keeps an `Alt` suffix.
|
|
65
68
|
export * from "./ui/accordion";
|
|
66
69
|
export {
|
package/tokens/src/css/index.css
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* modal.css, tabs.css and tabs-pills.css need app motion variables
|
|
10
10
|
* (--modal-*, --tabs-dur, --tabs-ease) and are not included. Apps that use
|
|
11
11
|
* the tailwind preset must not import animations.css; import the other
|
|
12
|
-
* files one by one instead.
|
|
12
|
+
* files one by one instead. nav-icons.css styles the animated nav icons.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
@import "./base.css";
|
|
@@ -17,3 +17,4 @@
|
|
|
17
17
|
@import "./utilities.css";
|
|
18
18
|
@import "./scrollbar-thin.css";
|
|
19
19
|
@import "./animations.css";
|
|
20
|
+
@import "./nav-icons.css";
|