@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/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`, active colour, `.t-tabs-pill`, small-screen `.t-tabs` scrolling. |
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 a `glass-panel-strong` fallback. Import it **or** `tokens.css`, never both. |
20
- | `@vvedantb/ui/tokens/css/index.css` | `src/css/index.css` | base + dialog + utilities + scrollbar-thin + animations, for new CSS-first apps. |
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`. `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.
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
- * Fallback fill for toasts, dialogs and hover cards (`glass-panel-strong`).
93
- * Equivalent to `bg-card/95 backdrop-blur-md`. An app may override it by
94
- * declaring its own `@utility glass-panel-strong` after this import.
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
- @utility glass-panel-strong {
97
- background-color: rgb(var(--card) / 0.95);
98
- -webkit-backdrop-filter: blur(12px);
99
- backdrop-filter: blur(12px);
97
+ @layer components {
98
+ .glass-panel-strong {
99
+ background-color: rgb(var(--popover));
100
+ }
100
101
  }
@@ -6,15 +6,18 @@
6
6
  * @import "@vvedantb/ui/tokens";
7
7
  * @import "@vvedantb/ui/tokens/css/index.css";
8
8
  *
9
- * modal.css, tabs.css and tabs-pills.css need app motion variables
10
- * (--modal-*, --tabs-dur, --tabs-ease) and are not included. Apps that use
11
- * the tailwind preset must not import animations.css; import the other
12
- * files one by one instead. nav-icons.css styles the animated nav icons.
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";
@@ -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
- * Needs on :root: --modal-open-dur, --modal-close-dur, --modal-scale,
6
- * --modal-scale-close, --modal-ease.
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 scrollbar properties are ignored by Chrome 121+ when webkit rules
6
- * exist, so the Firefox fallback is gated with @supports. Chrome gets the
7
- * 6px thumb; Firefox gets thin themed.
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 utilities {
11
- @supports not selector(::-webkit-scrollbar) {
12
- .scrollbar-thin {
13
- scrollbar-width: thin;
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
- .scrollbar-thin::-webkit-scrollbar {
25
- width: 6px;
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::-webkit-scrollbar-track {
29
- background: transparent;
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
- .scrollbar-thin::-webkit-scrollbar-thumb {
33
- background-color: color-mix(in oklch, var(--scrollbar) 80%, transparent);
34
- border-radius: 3px;
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
- .scrollbar-thin::-webkit-scrollbar-thumb:hover {
38
- background-color: color-mix(in oklch, var(--scrollbar) 100%, transparent);
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
  }
@@ -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). The .t-tabs
4
- * container rule stays in the app; it differs per app.
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, --tabs-text-active,
7
- * --tabs-pill-bg. tabs-pills.css sets the three colour variables.
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: color var(--tabs-dur) var(--tabs-ease);
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"],
@@ -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
+ }