@vvedantb/ui 0.13.1 → 0.15.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 +22 -5
- package/app/README.md +1 -1
- package/package.json +6 -1
- package/shell/src/app-shell-classes.ts +2 -2
- package/src/index.ts +2 -0
- package/src/ui/_picker-classes.ts +18 -0
- package/src/ui/calendar.tsx +161 -0
- package/src/ui/clearInputDissolve.ts +10 -5
- package/src/ui/date-picker.tsx +545 -0
- package/src/ui/dialog.tsx +5 -1
- package/src/ui/time-picker.tsx +12 -5
- package/tokens/README.md +30 -8
- package/tokens/src/css/clear-input.css +71 -0
- package/tokens/src/css/compat-shadcn-rgb.css +8 -7
- package/tokens/src/css/index.css +8 -5
- package/tokens/src/css/modal.css +3 -2
- package/tokens/src/css/motion.css +156 -0
- package/tokens/src/css/scrollbar-thin.css +54 -26
- package/tokens/src/css/tabs.css +16 -5
- package/tokens/src/tokens.css +52 -0
package/tokens/README.md
CHANGED
|
@@ -6,18 +6,20 @@ Design tokens and shared Tailwind v4 CSS for the vvedantb web apps. The CSS and
|
|
|
6
6
|
|
|
7
7
|
| Export | File | What it is |
|
|
8
8
|
| --- | --- | --- |
|
|
9
|
-
| `@vvedantb/ui/tokens`, `@vvedantb/ui/tokens/tokens.css` | `src/tokens.css` | Colour, radius, shadow, ease and font tokens (`@theme`), light/dark variables, `dark` variant, the radius utilities `rounded-surface` (`clamp(0.75rem, var(--radius), 1rem)`), `rounded-menu-item` (`min(var(--radius), 0.5rem)`) and `rounded-control` (`min(var(--radius), 0.625rem)`) (0.7.1; `rounded-surface` was in `utilities.css` before), `*` border and `body` defaults. 0.7.2 adds `--sidebar-accent` / `sidebar-accent` (sidebar nav row fill: light `var(--surface-tertiary)`, dark `oklch(22.64% 0 0)`). In 0.2.1 it also defines shadcn-style aliases (`primary`, `muted-foreground`, `secondary`, `destructive`, `card`, `popover`, `input`, `ring`) that point at the same tokens. |
|
|
9
|
+
| `@vvedantb/ui/tokens`, `@vvedantb/ui/tokens/tokens.css` | `src/tokens.css` | Colour, radius, shadow, ease and font tokens (`@theme`), light/dark variables, `dark` variant, the radius utilities `rounded-surface` (`clamp(0.75rem, var(--radius), 1rem)`), `rounded-menu-item` (`min(var(--radius), 0.5rem)`) and `rounded-control` (`min(var(--radius), 0.625rem)`) (0.7.1; `rounded-surface` was in `utilities.css` before), `*` border and `body` defaults. 0.7.2 adds `--sidebar-accent` / `sidebar-accent` (sidebar nav row fill: light `var(--surface-tertiary)`, dark `oklch(22.64% 0 0)`). In 0.2.1 it also defines shadcn-style aliases (`primary`, `muted-foreground`, `secondary`, `destructive`, `card`, `popover`, `input`, `ring`) that point at the same tokens. From 0.15.0 it imports `clear-input.css`, `scrollbar-thin.css` and `motion.css`, sets the `--modal-*` and `--tabs-*` motion variables on `:root`, and defines `.glass-panel-strong` (`var(--overlay)`) and `.sidebar-section-label`. |
|
|
10
10
|
| `tokens` and `Tokens` from `@vvedantb/ui` | `src/native.ts` | JS colours, radii and spacing for React Native. `@vvedantb/native-ui` keeps its own copy in `src/tokens.ts`; a test keeps the two equal. |
|
|
11
11
|
| `@vvedantb/ui/tokens/css/base.css` | `src/css/base.css` | `html` touch/accent, `::selection`, control transitions, pointer cursor, range accent (`@layer base`). |
|
|
12
12
|
| `@vvedantb/ui/tokens/css/dialog.css` | `src/css/dialog.css` | Unlayered `max-width: none` for `@vvedantb/ui` compact (bottom-sheet) dialogs. |
|
|
13
13
|
| `@vvedantb/ui/tokens/css/modal.css` | `src/css/modal.css` | `.t-modal`, `.is-open`, `.is-closing` and `t-modal-presence-wait` (`@layer components`). |
|
|
14
|
-
| `@vvedantb/ui/tokens/css/tabs.css` | `src/css/tabs.css` | `.t-tab
|
|
14
|
+
| `@vvedantb/ui/tokens/css/tabs.css` | `src/css/tabs.css` | `.t-tabs` container (0.15.0), `.t-tab` with its press scale, active colour, `.t-tabs-pill`, small-screen `.t-tabs` scrolling. |
|
|
15
15
|
| `@vvedantb/ui/tokens/css/tabs-pills.css` | `src/css/tabs-pills.css` | `--tabs-*` colour variables, `.t-tabs--segmented`, `.t-tabs--pills` and pill hover / active fills. |
|
|
16
16
|
| `@vvedantb/ui/tokens/css/utilities.css` | `src/css/utilities.css` | `@utility hit-target` and `@utility reveal-on-hover`. `rounded-surface` moved to `tokens.css` in 0.7.1. |
|
|
17
|
-
| `@vvedantb/ui/tokens/css/scrollbar-thin.css` | `src/css/scrollbar-thin.css` | `.scrollbar-thin` (`@layer utilities`, always emitted). |
|
|
17
|
+
| `@vvedantb/ui/tokens/css/scrollbar-thin.css` | `src/css/scrollbar-thin.css` | `.scrollbar-thin` (`@layer utilities`, always emitted). Imported by `tokens.css` from 0.15.0. |
|
|
18
|
+
| `@vvedantb/ui/tokens/css/clear-input.css` | `src/css/clear-input.css` | `ClearInput` layers (`.t-clear*`) and the `--clear-*` / `--glow-*` timings. Imported by `tokens.css`. |
|
|
19
|
+
| `@vvedantb/ui/tokens/css/motion.css` | `src/css/motion.css` | `.ui-page-enter`, `.ui-enter`, `.ui-enter-up`, `.ui-stagger`, their keyframes and reduced-motion rules, and the `html[data-page-motion="off"]` overrides for `PageMotionProvider`. Imported by `tokens.css`. |
|
|
18
20
|
| `@vvedantb/ui/tokens/css/animations.css` | `src/css/animations.css` | `animate-aurora`, `animate-fade-in-up`, `animate-indeterminate` as `@theme` (CSS-first apps). |
|
|
19
|
-
| `@vvedantb/ui/tokens/css/compat-shadcn-rgb.css`, `@vvedantb/ui/tokens/compat/shadcn-rgb.css` | `src/css/compat-shadcn-rgb.css` | Replaces `tokens.css` for apps with shadcn-style `R G B` channel variables. Defines only the UI-only `--color-*` names as `rgb(var(--x) / alpha)` references, `--radius-field`, `--scrollbar`, `--field-radius`, the same three radius utilities as `tokens.css` and
|
|
20
|
-
| `@vvedantb/ui/tokens/css/index.css` | `src/css/index.css` | base + dialog +
|
|
21
|
+
| `@vvedantb/ui/tokens/css/compat-shadcn-rgb.css`, `@vvedantb/ui/tokens/compat/shadcn-rgb.css` | `src/css/compat-shadcn-rgb.css` | Replaces `tokens.css` for apps with shadcn-style `R G B` channel variables. Defines only the UI-only `--color-*` names as `rgb(var(--x) / alpha)` references, `--radius-field`, `--scrollbar`, `--field-radius`, the same three radius utilities as `tokens.css` and `.glass-panel-strong` (solid `--popover` from 0.15.0). Import it **or** `tokens.css`, never both. |
|
|
22
|
+
| `@vvedantb/ui/tokens/css/index.css` | `src/css/index.css` | base + dialog + modal + tabs + tabs-pills + utilities + animations + nav-icons, for new CSS-first apps (`tokens.css` brings scrollbar-thin). |
|
|
21
23
|
| `@vvedantb/ui/tokens/tailwind-preset` | `src/tailwind-preset.mjs` (+ `.d.mts`) | Tailwind preset for apps that load a JS config with `@config`. |
|
|
22
24
|
|
|
23
25
|
Every file was extracted because the same rule (same at-rule context, selector and body) sits in 3 or more of the five apps. The measured duplication is in `verify/ANALYSIS.md`, and the proof that each app compiles to the same CSS after migrating is in `verify/RESULTS.md`. Blocks that only 2 apps share, or that drift in a way that would change an app's CSS, stay in the apps; `verify/ANALYSIS.md` lists them.
|
|
@@ -35,6 +37,7 @@ CSS-first app (no `tailwind.config`):
|
|
|
35
37
|
@import "@vvedantb/ui/tokens/css/modal.css";
|
|
36
38
|
@import "@vvedantb/ui/tokens/css/tabs.css";
|
|
37
39
|
/* ...app @layer components rules... */
|
|
40
|
+
/* no --modal-* / --tabs-* needed: tokens.css sets them */
|
|
38
41
|
```
|
|
39
42
|
|
|
40
43
|
App with a JS config (`@config "../tailwind.config.ts"`):
|
|
@@ -65,17 +68,18 @@ Tailwind inlines an `@import` where it sits, even in the middle of a file (it mu
|
|
|
65
68
|
| --- | --- | --- |
|
|
66
69
|
| `base.css` | `--accent`, `--foreground` | tokens.css or the app theme |
|
|
67
70
|
| `dialog.css` | none | |
|
|
68
|
-
| `modal.css` | `--modal-open-dur`, `--modal-close-dur`, `--modal-scale`, `--modal-scale-close`, `--modal-ease` | app `:root` |
|
|
69
|
-
| `tabs.css` | `--tabs-dur`, `--tabs-ease` (app `:root`); `--tabs-text-muted`, `--tabs-text-active`, `--tabs-pill-bg` | colours: tabs-pills.css or the app theme |
|
|
71
|
+
| `modal.css` | `--modal-open-dur`, `--modal-close-dur`, `--modal-scale`, `--modal-scale-close`, `--modal-ease` | tokens.css (0.15.0), or app `:root` with the compat file |
|
|
72
|
+
| `tabs.css` | `--tabs-dur`, `--tabs-ease` (tokens.css, or app `:root` with the compat file); `--tabs-text-muted`, `--tabs-text-active`, `--tabs-pill-bg` | colours: tabs-pills.css or the app theme |
|
|
70
73
|
| `tabs-pills.css` | `--muted`, `--segment`, `--segment-foreground`, `--default` | tokens.css or the app theme |
|
|
71
74
|
| `utilities.css` | `--breakpoint-sm` (Tailwind default or app theme) | Tailwind |
|
|
72
75
|
| `scrollbar-thin.css` | `--scrollbar` | tokens.css or the app theme |
|
|
76
|
+
| `clear-input.css`, `motion.css` | none (own `:root` defaults) | |
|
|
73
77
|
| `animations.css` | none | |
|
|
74
78
|
| `tailwind-preset` | the CSS variables named in its colour map, `--radius`, `--field-radius` | tokens.css or the app theme |
|
|
75
79
|
|
|
76
80
|
### Using an existing shadcn RGB theme
|
|
77
81
|
|
|
78
|
-
See the package `README.md`. With the compat file, `base.css`, `scrollbar-thin.css`, `utilities.css`, `dialog.css`, `modal.css` and `tabs.css` need no `tokens.css
|
|
82
|
+
See the package `README.md`. With the compat file, `base.css`, `scrollbar-thin.css`, `clear-input.css`, `motion.css`, `utilities.css`, `dialog.css`, `modal.css` and `tabs.css` need no `tokens.css` (`modal.css` and `tabs.css` then need the `--modal-*` / `--tabs-*` values on the app `:root`). `base.css` reads `--accent` and `--foreground` and `tabs-pills.css` reads `--muted`, `--segment` and `--default`. Those are not channel-compatible, so skip `tabs-pills.css` and `index.css` (which includes `base.css`) unless the app defines those variables as full colours.
|
|
79
83
|
|
|
80
84
|
### Parameters
|
|
81
85
|
|
|
@@ -86,6 +90,24 @@ Where apps share a rule but drifted in one value, the value is a custom property
|
|
|
86
90
|
| `base.css` | `--tokens-control-transition-property` | `background-color, border-color, color, box-shadow, transform, opacity` | app-c: `background-color, color, transform, opacity` |
|
|
87
91
|
| `base.css` | `--tokens-control-transition-duration` | `240ms` | app-c: `100ms` |
|
|
88
92
|
|
|
93
|
+
From 0.15.0 the files that moved out of the apps take `:root` variables too. Every rule is in a Tailwind layer, so an app rule for the same selector (unlayered, or later in the same layer) also wins.
|
|
94
|
+
|
|
95
|
+
| File | Property | Default |
|
|
96
|
+
| --- | --- | --- |
|
|
97
|
+
| `tokens.css` | `--modal-open-dur`, `--modal-close-dur` | `180ms`, `120ms` |
|
|
98
|
+
| `tokens.css` | `--modal-scale`, `--modal-scale-close` | `0.96`, `0.96` |
|
|
99
|
+
| `tokens.css` | `--modal-ease`, `--tabs-ease` | `cubic-bezier(0.22, 1, 0.36, 1)` |
|
|
100
|
+
| `tokens.css` | `--tabs-dur` | `200ms` |
|
|
101
|
+
| `tokens.css` | `--sidebar-section-label-mix`, `--sidebar-section-label-hover-mix` | `55%`, `80%` (share of `--muted`) |
|
|
102
|
+
| `scrollbar-thin.css` | `--scrollbar-thin-rest`, `--scrollbar-thin-hover` | `40%`, `80%` (share of `--scrollbar`) |
|
|
103
|
+
| `clear-input.css` | `--clear-dur`, `--clear-out-dur`, `--clear-in-dur` | `1000ms`, `400ms`, `400ms` |
|
|
104
|
+
| `clear-input.css` | `--clear-out-fly`, `--clear-in-fly` | `12px`, `12px` |
|
|
105
|
+
| `clear-input.css` | `--clear-out-ease`, `--clear-in-ease` | `cubic-bezier(0.22, 1, 0.36, 1)` |
|
|
106
|
+
| `clear-input.css` | `--glow-delay`, `--glow-peak-at`, `--glow-opacity`, `--glow-spread` | `50ms`, `0.15`, `0.85`, `1.5` |
|
|
107
|
+
| `motion.css` | `--ui-enter-ease` | `cubic-bezier(0.22, 1, 0.36, 1)` |
|
|
108
|
+
| `motion.css` | `--ui-page-enter-dur`, `--ui-enter-dur`, `--ui-enter-up-dur` | `160ms`, `200ms`, `220ms` |
|
|
109
|
+
| `motion.css` | `--ui-stagger-step` | `40ms` (children 1 to 8) |
|
|
110
|
+
|
|
89
111
|
## Develop
|
|
90
112
|
|
|
91
113
|
```sh
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @vvedantb/ui/tokens/css/clear-input.css: layers for @vvedantb/ui
|
|
3
|
+
* ClearInput (`.t-clear`, mirror, placeholder and glow), the transitions.dev
|
|
4
|
+
* input clear with dissolve. tokens.css imports it.
|
|
5
|
+
*
|
|
6
|
+
* clearInputDissolve reads the variables below from `html`. Set any of them
|
|
7
|
+
* on the app `:root` to change the timing (numbers in ms or px):
|
|
8
|
+
* --clear-dur, --clear-out-dur, --clear-in-dur, --clear-out-fly,
|
|
9
|
+
* --clear-in-fly, --clear-out-ease, --clear-in-ease, --glow-delay,
|
|
10
|
+
* --glow-peak-at, --glow-opacity, --glow-spread
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
@layer base {
|
|
14
|
+
:root {
|
|
15
|
+
--clear-dur: 1000ms;
|
|
16
|
+
--clear-out-dur: 400ms;
|
|
17
|
+
--clear-in-dur: 400ms;
|
|
18
|
+
--clear-out-fly: 12px;
|
|
19
|
+
--clear-in-fly: 12px;
|
|
20
|
+
--clear-out-ease: cubic-bezier(0.22, 1, 0.36, 1);
|
|
21
|
+
--clear-in-ease: cubic-bezier(0.22, 1, 0.36, 1);
|
|
22
|
+
--glow-delay: 50ms;
|
|
23
|
+
--glow-peak-at: 0.15;
|
|
24
|
+
--glow-opacity: 0.85;
|
|
25
|
+
--glow-spread: 1.5;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@layer components {
|
|
30
|
+
.t-clear {
|
|
31
|
+
position: relative;
|
|
32
|
+
overflow: hidden;
|
|
33
|
+
}
|
|
34
|
+
.t-clear-mirror,
|
|
35
|
+
.t-clear-placeholder {
|
|
36
|
+
position: absolute;
|
|
37
|
+
inset: 0;
|
|
38
|
+
display: flex;
|
|
39
|
+
align-items: center;
|
|
40
|
+
pointer-events: none;
|
|
41
|
+
white-space: nowrap;
|
|
42
|
+
overflow: hidden;
|
|
43
|
+
z-index: 2;
|
|
44
|
+
}
|
|
45
|
+
.t-clear-mirror {
|
|
46
|
+
opacity: 0;
|
|
47
|
+
}
|
|
48
|
+
.t-clear.has-value .t-clear-mirror,
|
|
49
|
+
.t-clear.is-clearing .t-clear-mirror {
|
|
50
|
+
opacity: 1;
|
|
51
|
+
}
|
|
52
|
+
.t-clear.has-value > input,
|
|
53
|
+
.t-clear.is-clearing > input {
|
|
54
|
+
-webkit-text-fill-color: transparent;
|
|
55
|
+
}
|
|
56
|
+
.t-clear.has-value .t-clear-placeholder {
|
|
57
|
+
opacity: 0;
|
|
58
|
+
}
|
|
59
|
+
.t-clear-glow {
|
|
60
|
+
position: absolute;
|
|
61
|
+
inset: 0;
|
|
62
|
+
pointer-events: none;
|
|
63
|
+
opacity: 0;
|
|
64
|
+
z-index: 3;
|
|
65
|
+
mix-blend-mode: multiply;
|
|
66
|
+
}
|
|
67
|
+
.dark .t-clear-glow,
|
|
68
|
+
[data-theme="dark"] .t-clear-glow {
|
|
69
|
+
mix-blend-mode: screen;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -89,12 +89,13 @@
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
/*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
92
|
+
* Fill for toasts, dialogs and hover cards (`glass-panel-strong`): the solid
|
|
93
|
+
* popover colour, as `bg-overlay`, matching tokens.css. Any app
|
|
94
|
+
* `.glass-panel-strong` rule (unlayered, or in @layer components after this
|
|
95
|
+
* import) wins.
|
|
95
96
|
*/
|
|
96
|
-
@
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
@layer components {
|
|
98
|
+
.glass-panel-strong {
|
|
99
|
+
background-color: rgb(var(--popover));
|
|
100
|
+
}
|
|
100
101
|
}
|
package/tokens/src/css/index.css
CHANGED
|
@@ -6,15 +6,18 @@
|
|
|
6
6
|
* @import "@vvedantb/ui/tokens";
|
|
7
7
|
* @import "@vvedantb/ui/tokens/css/index.css";
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* the tailwind preset must not import animations.css;
|
|
12
|
-
* files one by one instead. nav-icons.css styles the
|
|
9
|
+
* tokens.css already brings scrollbar-thin.css, clear-input.css and
|
|
10
|
+
* motion.css, and the --modal-* / --tabs-* variables modal.css and tabs.css
|
|
11
|
+
* read. Apps that use the tailwind preset must not import animations.css;
|
|
12
|
+
* import the other files one by one instead. nav-icons.css styles the
|
|
13
|
+
* animated nav icons.
|
|
13
14
|
*/
|
|
14
15
|
|
|
15
16
|
@import "./base.css";
|
|
16
17
|
@import "./dialog.css";
|
|
18
|
+
@import "./modal.css";
|
|
19
|
+
@import "./tabs.css";
|
|
20
|
+
@import "./tabs-pills.css";
|
|
17
21
|
@import "./utilities.css";
|
|
18
|
-
@import "./scrollbar-thin.css";
|
|
19
22
|
@import "./animations.css";
|
|
20
23
|
@import "./nav-icons.css";
|
package/tokens/src/css/modal.css
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* @vvedantb/ui/tokens/css/modal.css: .t-modal open / close transition used by
|
|
3
3
|
* @vvedantb/ui Dialog (transitions.dev pattern).
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* --modal-scale-close
|
|
5
|
+
* Reads --modal-open-dur, --modal-close-dur, --modal-scale,
|
|
6
|
+
* --modal-scale-close and --modal-ease. tokens.css sets them on :root; set
|
|
7
|
+
* them on the app :root to change them (apps on compat-shadcn-rgb.css must).
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
@layer components {
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @vvedantb/ui/tokens/css/motion.css: page and list enter animations
|
|
3
|
+
* (`.ui-page-enter`, `.ui-enter`, `.ui-enter-up`, `.ui-stagger`) and the
|
|
4
|
+
* overrides for PageMotionProvider's `html[data-page-motion="off"]`.
|
|
5
|
+
* tokens.css imports it. Keyframe names (`ui-*`) do not clash with
|
|
6
|
+
* animations.css or the tailwind preset.
|
|
7
|
+
*
|
|
8
|
+
* Set on :root to change (defaults shown):
|
|
9
|
+
* --ui-enter-ease: cubic-bezier(0.22, 1, 0.36, 1)
|
|
10
|
+
* --ui-page-enter-dur: 160ms
|
|
11
|
+
* --ui-enter-dur: 200ms
|
|
12
|
+
* --ui-enter-up-dur: 220ms
|
|
13
|
+
* --ui-stagger-step: 40ms (delay between the first 8 children)
|
|
14
|
+
*
|
|
15
|
+
* The app keeps its own view transition setup (names, timings, morphs); this
|
|
16
|
+
* file only turns those animations off for reduced motion and page motion off.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
@layer base {
|
|
20
|
+
:root {
|
|
21
|
+
--ui-enter-ease: cubic-bezier(0.22, 1, 0.36, 1);
|
|
22
|
+
--ui-page-enter-dur: 160ms;
|
|
23
|
+
--ui-enter-dur: 200ms;
|
|
24
|
+
--ui-enter-up-dur: 220ms;
|
|
25
|
+
--ui-stagger-step: 40ms;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@layer components {
|
|
30
|
+
.ui-page-enter {
|
|
31
|
+
animation: ui-page-enter var(--ui-page-enter-dur) var(--ui-enter-ease)
|
|
32
|
+
backwards;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.ui-enter,
|
|
36
|
+
.ui-stagger > * {
|
|
37
|
+
animation: ui-enter var(--ui-enter-dur) var(--ui-enter-ease) backwards;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.ui-enter-up {
|
|
41
|
+
animation: ui-enter-up var(--ui-enter-up-dur) var(--ui-enter-ease)
|
|
42
|
+
backwards;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.ui-stagger > *:nth-child(2) {
|
|
46
|
+
animation-delay: calc(var(--ui-stagger-step) * 1);
|
|
47
|
+
}
|
|
48
|
+
.ui-stagger > *:nth-child(3) {
|
|
49
|
+
animation-delay: calc(var(--ui-stagger-step) * 2);
|
|
50
|
+
}
|
|
51
|
+
.ui-stagger > *:nth-child(4) {
|
|
52
|
+
animation-delay: calc(var(--ui-stagger-step) * 3);
|
|
53
|
+
}
|
|
54
|
+
.ui-stagger > *:nth-child(5) {
|
|
55
|
+
animation-delay: calc(var(--ui-stagger-step) * 4);
|
|
56
|
+
}
|
|
57
|
+
.ui-stagger > *:nth-child(6) {
|
|
58
|
+
animation-delay: calc(var(--ui-stagger-step) * 5);
|
|
59
|
+
}
|
|
60
|
+
.ui-stagger > *:nth-child(7) {
|
|
61
|
+
animation-delay: calc(var(--ui-stagger-step) * 6);
|
|
62
|
+
}
|
|
63
|
+
.ui-stagger > *:nth-child(8) {
|
|
64
|
+
animation-delay: calc(var(--ui-stagger-step) * 7);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@keyframes ui-page-enter {
|
|
68
|
+
from {
|
|
69
|
+
opacity: 0;
|
|
70
|
+
transform: translateY(4px);
|
|
71
|
+
}
|
|
72
|
+
to {
|
|
73
|
+
opacity: 1;
|
|
74
|
+
transform: translateY(0);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@keyframes ui-enter {
|
|
79
|
+
from {
|
|
80
|
+
opacity: 0;
|
|
81
|
+
transform: translateY(6px);
|
|
82
|
+
}
|
|
83
|
+
to {
|
|
84
|
+
opacity: 1;
|
|
85
|
+
transform: translateY(0);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@keyframes ui-enter-up {
|
|
90
|
+
from {
|
|
91
|
+
opacity: 0;
|
|
92
|
+
transform: translateY(12px);
|
|
93
|
+
}
|
|
94
|
+
to {
|
|
95
|
+
opacity: 1;
|
|
96
|
+
transform: translateY(0);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@keyframes ui-fade-only {
|
|
101
|
+
from {
|
|
102
|
+
opacity: 0;
|
|
103
|
+
}
|
|
104
|
+
to {
|
|
105
|
+
opacity: 1;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@media (prefers-reduced-motion: reduce) {
|
|
110
|
+
.ui-page-enter,
|
|
111
|
+
.ui-enter,
|
|
112
|
+
.ui-enter-up,
|
|
113
|
+
.ui-stagger > * {
|
|
114
|
+
animation-duration: 1ms;
|
|
115
|
+
animation-name: ui-fade-only;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
::view-transition-group(*),
|
|
119
|
+
::view-transition-old(*),
|
|
120
|
+
::view-transition-new(*) {
|
|
121
|
+
animation: none !important;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/*
|
|
126
|
+
* PageMotionProvider `disabled` sets `html[data-page-motion="off"]`. Page and
|
|
127
|
+
* route enters, list stagger, `animate-in` / `animate-out`, the modal and
|
|
128
|
+
* tab pill transitions and view transitions stop. Spinners keep running and
|
|
129
|
+
* the modal still reaches its open state, so dialogs still appear.
|
|
130
|
+
*/
|
|
131
|
+
html[data-page-motion="off"] .ui-page-enter,
|
|
132
|
+
html[data-page-motion="off"] .ui-enter,
|
|
133
|
+
html[data-page-motion="off"] .ui-enter-up,
|
|
134
|
+
html[data-page-motion="off"] .ui-stagger > * {
|
|
135
|
+
animation-duration: 1ms;
|
|
136
|
+
animation-name: ui-fade-only;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
html[data-page-motion="off"] .animate-in,
|
|
140
|
+
html[data-page-motion="off"] .animate-out {
|
|
141
|
+
animation-duration: 0s !important;
|
|
142
|
+
animation-delay: 0s !important;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
html[data-page-motion="off"] .t-modal,
|
|
146
|
+
html[data-page-motion="off"] .t-modal.is-closing,
|
|
147
|
+
html[data-page-motion="off"] .t-tabs-pill {
|
|
148
|
+
transition-duration: 0s !important;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
html[data-page-motion="off"]::view-transition-group(*),
|
|
152
|
+
html[data-page-motion="off"]::view-transition-old(*),
|
|
153
|
+
html[data-page-motion="off"]::view-transition-new(*) {
|
|
154
|
+
animation: none !important;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -1,40 +1,68 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* @vvedantb/ui/tokens/css/scrollbar-thin.css: `.scrollbar-thin` (always emitted,
|
|
3
|
-
* plain @layer utilities). Needs --scrollbar.
|
|
3
|
+
* plain @layer utilities). Needs --scrollbar. tokens.css imports it; apps on
|
|
4
|
+
* compat-shadcn-rgb.css import it themselves.
|
|
4
5
|
*
|
|
5
|
-
* Standard
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Standard properties first (Chrome 121+, Firefox). Chrome ignores webkit
|
|
7
|
+
* scrollbar rules once `scrollbar-color` is set, so the 6px webkit thumb is
|
|
8
|
+
* only a fallback for browsers without `scrollbar-color`.
|
|
9
|
+
*
|
|
10
|
+
* Thumb strength (share of --scrollbar), set on :root to change:
|
|
11
|
+
* --scrollbar-thin-rest: 40%
|
|
12
|
+
* --scrollbar-thin-hover: 80%
|
|
8
13
|
*/
|
|
9
14
|
|
|
10
|
-
@layer
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
scrollbar-color: color-mix(in oklch, var(--scrollbar) 80%, transparent)
|
|
15
|
-
transparent;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
.scrollbar-thin:hover {
|
|
19
|
-
scrollbar-color: color-mix(in oklch, var(--scrollbar) 100%, transparent)
|
|
20
|
-
transparent;
|
|
21
|
-
}
|
|
15
|
+
@layer base {
|
|
16
|
+
:root {
|
|
17
|
+
--scrollbar-thin-rest: 40%;
|
|
18
|
+
--scrollbar-thin-hover: 80%;
|
|
22
19
|
}
|
|
20
|
+
}
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
@layer utilities {
|
|
23
|
+
.scrollbar-thin {
|
|
24
|
+
scrollbar-width: thin;
|
|
25
|
+
scrollbar-color: color-mix(
|
|
26
|
+
in oklch,
|
|
27
|
+
var(--scrollbar) var(--scrollbar-thin-rest),
|
|
28
|
+
transparent
|
|
29
|
+
)
|
|
30
|
+
transparent;
|
|
26
31
|
}
|
|
27
32
|
|
|
28
|
-
.scrollbar-thin
|
|
29
|
-
|
|
33
|
+
.scrollbar-thin:hover {
|
|
34
|
+
scrollbar-color: color-mix(
|
|
35
|
+
in oklch,
|
|
36
|
+
var(--scrollbar) var(--scrollbar-thin-hover),
|
|
37
|
+
transparent
|
|
38
|
+
)
|
|
39
|
+
transparent;
|
|
30
40
|
}
|
|
31
41
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
42
|
+
@supports not (scrollbar-color: auto) {
|
|
43
|
+
.scrollbar-thin::-webkit-scrollbar {
|
|
44
|
+
width: 6px;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.scrollbar-thin::-webkit-scrollbar-track {
|
|
48
|
+
background: transparent;
|
|
49
|
+
}
|
|
36
50
|
|
|
37
|
-
|
|
38
|
-
|
|
51
|
+
.scrollbar-thin::-webkit-scrollbar-thumb {
|
|
52
|
+
background-color: color-mix(
|
|
53
|
+
in oklch,
|
|
54
|
+
var(--scrollbar) var(--scrollbar-thin-rest),
|
|
55
|
+
transparent
|
|
56
|
+
);
|
|
57
|
+
border-radius: 3px;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.scrollbar-thin:hover::-webkit-scrollbar-thumb {
|
|
61
|
+
background-color: color-mix(
|
|
62
|
+
in oklch,
|
|
63
|
+
var(--scrollbar) var(--scrollbar-thin-hover),
|
|
64
|
+
transparent
|
|
65
|
+
);
|
|
66
|
+
}
|
|
39
67
|
}
|
|
40
68
|
}
|
package/tokens/src/css/tabs.css
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* @vvedantb/ui/tokens/css/tabs.css: sliding tab bar (.t-tab, .t-tabs-pill)
|
|
3
|
-
* used by @vvedantb/ui tabsSliding (transitions.dev pattern)
|
|
4
|
-
* container rule
|
|
3
|
+
* used by @vvedantb/ui tabsSliding (transitions.dev pattern), and the .t-tabs
|
|
4
|
+
* container. An app rule for any of them wins (@layer components).
|
|
5
5
|
*
|
|
6
|
-
* Needs: --tabs-dur, --tabs-ease, --tabs-text-muted,
|
|
7
|
-
* --tabs-pill-bg. tabs-pills.css sets the three colour
|
|
6
|
+
* Needs: --tabs-dur, --tabs-ease (defaults in tokens.css), --tabs-text-muted,
|
|
7
|
+
* --tabs-text-active, --tabs-pill-bg. tabs-pills.css sets the three colour
|
|
8
|
+
* variables.
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
11
|
@layer components {
|
|
12
|
+
.t-tabs {
|
|
13
|
+
position: relative;
|
|
14
|
+
display: inline-flex;
|
|
15
|
+
align-items: center;
|
|
16
|
+
}
|
|
11
17
|
@media (max-width: 639px) {
|
|
12
18
|
.t-tabs {
|
|
13
19
|
justify-content: safe center;
|
|
@@ -29,7 +35,12 @@
|
|
|
29
35
|
cursor: pointer;
|
|
30
36
|
border-radius: 48px;
|
|
31
37
|
z-index: 1;
|
|
32
|
-
transition:
|
|
38
|
+
transition:
|
|
39
|
+
color var(--tabs-dur) var(--tabs-ease),
|
|
40
|
+
transform 100ms ease-out;
|
|
41
|
+
}
|
|
42
|
+
.t-tab:active {
|
|
43
|
+
transform: scale(0.97);
|
|
33
44
|
}
|
|
34
45
|
.t-tab:not([aria-selected="true"]):not([data-state="active"]):hover,
|
|
35
46
|
.t-tab[aria-selected="true"],
|
package/tokens/src/tokens.css
CHANGED
|
@@ -16,8 +16,19 @@
|
|
|
16
16
|
* @import url("https://fonts.googleapis.com/css2?family=Instrument+Sans:ital,wght@0,400..700;1,400..700&display=swap");
|
|
17
17
|
*
|
|
18
18
|
* Without it, --font-sans falls back to system-ui.
|
|
19
|
+
*
|
|
20
|
+
* From 0.15.0 this file also brings, on by default: the ClearInput layers,
|
|
21
|
+
* `.scrollbar-thin`, the `.ui-enter` family with the PageMotionProvider
|
|
22
|
+
* overrides, `.glass-panel-strong`, `.sidebar-section-label` and the
|
|
23
|
+
* modal / tabs motion variables. Every rule sits in a Tailwind layer and the
|
|
24
|
+
* timings and strengths are `:root` variables, so app rules and app `:root`
|
|
25
|
+
* values win.
|
|
19
26
|
*/
|
|
20
27
|
|
|
28
|
+
@import "./css/clear-input.css";
|
|
29
|
+
@import "./css/scrollbar-thin.css";
|
|
30
|
+
@import "./css/motion.css";
|
|
31
|
+
|
|
21
32
|
@custom-variant dark (&:where(.dark, .dark *));
|
|
22
33
|
|
|
23
34
|
@theme {
|
|
@@ -226,3 +237,44 @@
|
|
|
226
237
|
-moz-osx-font-smoothing: grayscale;
|
|
227
238
|
}
|
|
228
239
|
}
|
|
240
|
+
|
|
241
|
+
/* Motion for css/modal.css (`.t-modal`) and css/tabs.css (`.t-tab`,
|
|
242
|
+
`.t-tabs-pill`). Set any of them on the app `:root` to change it. */
|
|
243
|
+
@layer base {
|
|
244
|
+
:root {
|
|
245
|
+
--modal-open-dur: 180ms;
|
|
246
|
+
--modal-close-dur: 120ms;
|
|
247
|
+
--modal-scale: 0.96;
|
|
248
|
+
--modal-scale-close: 0.96;
|
|
249
|
+
--modal-ease: cubic-bezier(0.22, 1, 0.36, 1);
|
|
250
|
+
--tabs-dur: 200ms;
|
|
251
|
+
--tabs-ease: cubic-bezier(0.22, 1, 0.36, 1);
|
|
252
|
+
--sidebar-section-label-mix: 55%;
|
|
253
|
+
--sidebar-section-label-hover-mix: 80%;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
@layer components {
|
|
258
|
+
/* Solid overlay fill for Dialog, SonnerToaster, SlideOverPanel and
|
|
259
|
+
SessionsHoverCard `surface="glass"`. Same as `bg-overlay`. */
|
|
260
|
+
.glass-panel-strong {
|
|
261
|
+
background-color: var(--overlay);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/* Shell section label (`sidebarSectionLabelClass`). */
|
|
265
|
+
.sidebar-section-label {
|
|
266
|
+
color: color-mix(
|
|
267
|
+
in oklch,
|
|
268
|
+
var(--muted) var(--sidebar-section-label-mix),
|
|
269
|
+
transparent
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.sidebar-section-label:hover {
|
|
274
|
+
color: color-mix(
|
|
275
|
+
in oklch,
|
|
276
|
+
var(--muted) var(--sidebar-section-label-hover-mix),
|
|
277
|
+
transparent
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
}
|