@vvedantb/ui 0.31.1 → 0.33.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 +20 -2
- package/package.json +3 -1
- package/src/ui/sheet.tsx +165 -19
- package/tokens/README.md +2 -0
- package/tokens/src/css/type-scale.css +61 -0
- package/tokens/src/tokens.css +4 -0
package/README.md
CHANGED
|
@@ -212,7 +212,10 @@ import { SessionsHoverCard } from "@vvedantb/ui";
|
|
|
212
212
|
## Panels, search and confirm
|
|
213
213
|
|
|
214
214
|
```tsx
|
|
215
|
-
import {
|
|
215
|
+
import {
|
|
216
|
+
ConfirmDialog, Drawer, DrawerContent, DrawerTitle, SearchInput, Sheet, SheetContent,
|
|
217
|
+
SheetTitle, SlideOverPanel,
|
|
218
|
+
} from "@vvedantb/ui";
|
|
216
219
|
|
|
217
220
|
<div className="relative">
|
|
218
221
|
<SlideOverPanel open={open} onClose={() => setOpen(false)} header={<h2>Details</h2>}>
|
|
@@ -220,6 +223,20 @@ import { ConfirmDialog, SearchInput, SlideOverPanel } from "@vvedantb/ui";
|
|
|
220
223
|
</SlideOverPanel>
|
|
221
224
|
</div>
|
|
222
225
|
|
|
226
|
+
<Sheet open={open} onOpenChange={setOpen} direction="right">
|
|
227
|
+
<SheetContent motionBlur surfaceClassName="p-4">
|
|
228
|
+
<SheetTitle>Details</SheetTitle>
|
|
229
|
+
Body
|
|
230
|
+
</SheetContent>
|
|
231
|
+
</Sheet>
|
|
232
|
+
|
|
233
|
+
{/* Drawer* names are aliases of Sheet* */}
|
|
234
|
+
<Drawer open={open} onOpenChange={setOpen}>
|
|
235
|
+
<DrawerContent motionBlur>
|
|
236
|
+
<DrawerTitle>Share</DrawerTitle>
|
|
237
|
+
</DrawerContent>
|
|
238
|
+
</Drawer>
|
|
239
|
+
|
|
223
240
|
<SearchInput value={query} onChange={setQuery} label="Search files" shortcut="/" />
|
|
224
241
|
|
|
225
242
|
<SearchInput variant="toolbar" value={query} onChange={setQuery} />
|
|
@@ -229,7 +246,8 @@ import { ConfirmDialog, SearchInput, SlideOverPanel } from "@vvedantb/ui";
|
|
|
229
246
|
<ConfirmDialog size="md" open={open} onOpenChange={setOpen} title="Delete item?" description="This cannot be undone." confirmLabel="Delete" variant="destructive" onConfirm={remove} />
|
|
230
247
|
```
|
|
231
248
|
|
|
232
|
-
- `
|
|
249
|
+
- `Sheet` / `Drawer` (vaul): mobile sheet and side drawer. `SheetContent` / `DrawerContent` take `side` (defaults to the root `direction`), opt-in `motionBlur` (default `false`) and `surfaceClassName`. With `motionBlur`, `@web-kits/motion-blur` blurs the panel only while it slides or is dragged (Echo, 180° shutter; off under `prefers-reduced-motion`). The moving element stays transparent; an inner `data-slot="sheet-surface"` holds the fill, shadow, corners and scroll. `className` then styles the moving host; `surfaceClassName` styles the surface. Without `motionBlur`, the DOM matches the previous single-element surface (`surfaceClassName` merges onto it after `className`). `Drawer*` exports are aliases of the `Sheet*` parts.
|
|
250
|
+
- `SlideOverPanel`: a right-side panel, absolutely positioned inside its nearest positioned parent. It is not a portal or dialog. It slides in with a spring and is hidden below `md`. Only the close button closes it (no Escape, no outside click). `width` is `"w-96"` (default) or `"w-80"`. `closeLabel` defaults to `"Close panel"`. `className` merges last. Does not use `motionBlur`.
|
|
233
251
|
- `SearchInput`: the search field. A `ClearInput` with a search icon on the left and the clear button on the right (shown while there is text; clearing calls `onChange("")`). `label` (default `"Search"`) is the `aria-label` and, without `placeholder`, the placeholder. `clearLabel` defaults to `"Clear search"`. `shortcut` (such as `"/"` or `"⌘K"`) shows a `Kbd` hint on the right while the value is empty. `size`: `"sm"` (`h-8`, default) or `"md"` (`h-10`). `variant`: `"default"` or `"toolbar"` (transparent until hover or focus, `rounded-field`, full width on mobile and `sm:w-44 md:w-52`). Other input props pass through; `enterKeyHint="search"`. `className` goes to the `<input>`, `wrapperClassName` to the wrapper, the ref to the `<input>`.
|
|
234
252
|
- `ToolbarSearchInput` (deprecated in 0.17.0): a wrapper over `<SearchInput variant="toolbar">` with `placeholder="Search..."`. `className` goes to the wrapper, `inputClassName` to the `<input>`.
|
|
235
253
|
- Field focus: every field (`Input`, `Textarea`, `Select` and `RadixSelect` triggers, `ClearInput`, `SearchInput`, `InputGroup`, date and time picker triggers) uses `fieldFocusClass`: the border turns `focus-border` and a 1px `ring-focus-ring` shows. Wrappers use `fieldFocusWithinClass` (`has-focus-visible:`). Both are exported for app fields.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vvedantb/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Shared React 19 UI for web apps: one main JavaScript entry (primitives, shell pieces, app helpers including the Convex and Clerk provider, VITE env parsing and ensure-user, artifact viewers over the @vvedantb/server artifacts core, native tokens), a client entry without @vvedantb/server, convex or Clerk for Chrome extensions, design-token CSS, shared Tailwind v4 CSS, a Tailwind preset and packaged Instrument Sans fonts.",
|
|
6
6
|
"type": "module",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"./tokens/css/border-beam.css": "./tokens/src/css/border-beam.css",
|
|
36
36
|
"./tokens/css/markdown.css": "./tokens/src/css/markdown.css",
|
|
37
37
|
"./tokens/css/marquee.css": "./tokens/src/css/marquee.css",
|
|
38
|
+
"./tokens/css/type-scale.css": "./tokens/src/css/type-scale.css",
|
|
38
39
|
"./tokens/compat/shadcn-rgb.css": "./tokens/src/css/compat-shadcn-rgb.css",
|
|
39
40
|
"./tokens/css/*.css": "./tokens/src/css/*.css",
|
|
40
41
|
"./tokens/tailwind-preset": {
|
|
@@ -88,6 +89,7 @@
|
|
|
88
89
|
"@dnd-kit/modifiers": "^9.0.0",
|
|
89
90
|
"@dnd-kit/sortable": "^10.0.0",
|
|
90
91
|
"@dnd-kit/utilities": "^3.2.2",
|
|
92
|
+
"@web-kits/motion-blur": "^0.1.0",
|
|
91
93
|
"dayjs": "^1.11.13",
|
|
92
94
|
"katex": "^0.16.45",
|
|
93
95
|
"media-chrome": "^4.14.0",
|
package/src/ui/sheet.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import * as React from "react";
|
|
4
|
+
import { useMotionBlur } from "@web-kits/motion-blur/react";
|
|
4
5
|
import { Drawer as DrawerPrimitive } from "vaul";
|
|
5
6
|
import { cn } from "../utils/cn";
|
|
6
7
|
import { assignRef } from "../utils/ref";
|
|
@@ -58,34 +59,132 @@ const SheetOverlay = React.forwardRef<
|
|
|
58
59
|
));
|
|
59
60
|
SheetOverlay.displayName = "SheetOverlay";
|
|
60
61
|
|
|
61
|
-
|
|
62
|
+
// Position and size. Stays on the element vaul moves.
|
|
63
|
+
const sheetSideLayoutClass: Record<SheetSide, string> = {
|
|
62
64
|
bottom:
|
|
63
|
-
"inset-x-0 bottom-[var(--overlay-vv-bottom,0px)] mt-24 flex max-h-[min(96dvh,calc(var(--overlay-vv-height,100dvh)-1.5rem))] h-auto flex-col
|
|
64
|
-
top: "inset-x-0 top-0 mb-24 flex h-auto max-h-[var(--overlay-vv-height,100dvh)] flex-col
|
|
65
|
-
left: "inset-y-2 left-2 right-auto flex h-auto w-80 max-w-[88vw] flex-col
|
|
66
|
-
right:
|
|
67
|
-
"inset-y-2 left-auto right-2 flex h-auto w-80 max-w-[88vw] flex-col rounded-2xl",
|
|
65
|
+
"inset-x-0 bottom-[var(--overlay-vv-bottom,0px)] mt-24 flex max-h-[min(96dvh,calc(var(--overlay-vv-height,100dvh)-1.5rem))] h-auto flex-col",
|
|
66
|
+
top: "inset-x-0 top-0 mb-24 flex h-auto max-h-[var(--overlay-vv-height,100dvh)] flex-col",
|
|
67
|
+
left: "inset-y-2 left-2 right-auto flex h-auto w-80 max-w-[88vw] flex-col",
|
|
68
|
+
right: "inset-y-2 left-auto right-2 flex h-auto w-80 max-w-[88vw] flex-col",
|
|
68
69
|
};
|
|
69
70
|
|
|
71
|
+
// Corners and safe-area padding. Go on the visible surface.
|
|
72
|
+
const sheetSideSurfaceClass: Record<SheetSide, string> = {
|
|
73
|
+
bottom: "rounded-t-2xl pb-[max(0.75rem,env(safe-area-inset-bottom,0px))]",
|
|
74
|
+
top: "rounded-b-2xl pt-[max(0.75rem,env(safe-area-inset-top,0px))]",
|
|
75
|
+
left: "rounded-2xl",
|
|
76
|
+
right: "rounded-2xl",
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Without `motionBlur`, one element is both the moving part and the surface.
|
|
80
|
+
const sheetContentClass =
|
|
81
|
+
"fixed z-50 overflow-y-auto bg-overlay/95 text-overlay-foreground smooth-shadow-ring-xl outline-hidden backdrop-blur-xl motion-reduce:transition-none";
|
|
82
|
+
|
|
83
|
+
// With `motionBlur`, the element vaul moves has to stay transparent, because the
|
|
84
|
+
// blur is drawn into its first child. It also stops clipping (no overflow), so
|
|
85
|
+
// the streak and the surface shadow can reach past its edges. `after:hidden`
|
|
86
|
+
// drops vaul's `::after` fill: it inherits the now transparent background, and
|
|
87
|
+
// the old `overflow-y-auto` clipped it anyway.
|
|
88
|
+
const sheetBlurHostClass =
|
|
89
|
+
"fixed z-50 outline-hidden motion-reduce:transition-none after:hidden";
|
|
90
|
+
|
|
91
|
+
// The visible surface inside it. It fills the host and is the scroll container.
|
|
92
|
+
const sheetBlurSurfaceClass =
|
|
93
|
+
"flex min-h-0 flex-auto flex-col overflow-y-auto bg-overlay/95 text-overlay-foreground smooth-shadow-ring-xl backdrop-blur-xl";
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* `@web-kits/motion-blur` reads transforms with `DOMMatrix` and the computed
|
|
97
|
+
* `translate`, polls `getAnimations()` and checks `matchMedia`, and throws
|
|
98
|
+
* without them. jsdom, happy-dom and browsers without the individual transform
|
|
99
|
+
* properties lack some of these, so the blur stays off there and the sheet
|
|
100
|
+
* still renders.
|
|
101
|
+
*/
|
|
102
|
+
function supportsMotionBlur(node: HTMLElement): boolean {
|
|
103
|
+
return (
|
|
104
|
+
typeof DOMMatrix === "function" &&
|
|
105
|
+
typeof window.matchMedia === "function" &&
|
|
106
|
+
typeof node.getAnimations === "function" &&
|
|
107
|
+
typeof window.getComputedStyle(node).translate === "string"
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const syncedEchoCopies = new WeakSet<Element>();
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Echo draws copies of the surface next to it, and a fresh copy starts at
|
|
115
|
+
* scroll 0. Give each copy the surface's scroll once, so a scrolled sheet does
|
|
116
|
+
* not flash its top while it closes.
|
|
117
|
+
*/
|
|
118
|
+
function syncEchoScroll(host: HTMLElement): void {
|
|
119
|
+
const surface = host.firstElementChild;
|
|
120
|
+
if (!(surface instanceof HTMLElement)) return;
|
|
121
|
+
for (let copy = surface.nextElementSibling; copy; copy = copy.nextElementSibling) {
|
|
122
|
+
if (syncedEchoCopies.has(copy) || copy.getAttribute("aria-hidden") !== "true") continue;
|
|
123
|
+
syncedEchoCopies.add(copy);
|
|
124
|
+
copy.scrollTop = surface.scrollTop;
|
|
125
|
+
copy.scrollLeft = surface.scrollLeft;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
70
129
|
const SheetContent = React.forwardRef<
|
|
71
130
|
React.ComponentRef<typeof DrawerPrimitive.Content>,
|
|
72
131
|
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content> & {
|
|
73
132
|
/** Defaults to the parent Sheet's `direction`. */
|
|
74
133
|
side?: SheetSide;
|
|
134
|
+
/**
|
|
135
|
+
* Blur the sheet while it slides in, out or is dragged, with
|
|
136
|
+
* `@web-kits/motion-blur` (Echo, 180° shutter). Off under
|
|
137
|
+
* `prefers-reduced-motion`. When `true`, the content element moves and
|
|
138
|
+
* stays transparent, and an inner `data-slot="sheet-surface"` div holds the
|
|
139
|
+
* fill, shadow, corners and scroll. `className` then styles the moving
|
|
140
|
+
* element only (size, position); use `surfaceClassName` for the surface.
|
|
141
|
+
* Default `false`.
|
|
142
|
+
*/
|
|
143
|
+
motionBlur?: boolean;
|
|
144
|
+
/**
|
|
145
|
+
* Classes for the visible surface: the inner div when `motionBlur` is on,
|
|
146
|
+
* otherwise the content element itself (after `className`).
|
|
147
|
+
*/
|
|
148
|
+
surfaceClassName?: string;
|
|
75
149
|
}
|
|
76
|
-
>(({ className, children, side: sideProp, ...props }, ref) => {
|
|
150
|
+
>(({ className, children, side: sideProp, motionBlur = false, surfaceClassName, ...props }, ref) => {
|
|
77
151
|
const direction = React.useContext(SheetDirectionContext);
|
|
78
152
|
const side = sideProp ?? direction;
|
|
79
153
|
const overlayCleanupRef = React.useRef<(() => void) | null>(null);
|
|
154
|
+
const blurCleanupRef = React.useRef<(() => void) | null>(null);
|
|
155
|
+
const hostRef = React.useRef<HTMLDivElement | null>(null);
|
|
156
|
+
|
|
157
|
+
const onBlurFrame = React.useCallback(() => {
|
|
158
|
+
if (hostRef.current) syncEchoScroll(hostRef.current);
|
|
159
|
+
}, []);
|
|
160
|
+
// Called on every render (hook rules); it does nothing until its ref is set.
|
|
161
|
+
const { ref: attachMotionBlur } = useMotionBlur({ onFrame: onBlurFrame });
|
|
162
|
+
|
|
163
|
+
const detachMotionBlur = React.useCallback(() => {
|
|
164
|
+
blurCleanupRef.current?.();
|
|
165
|
+
blurCleanupRef.current = null;
|
|
166
|
+
}, []);
|
|
167
|
+
|
|
168
|
+
const connectMotionBlur = React.useCallback(
|
|
169
|
+
(node: HTMLDivElement) => {
|
|
170
|
+
detachMotionBlur();
|
|
171
|
+
if (!supportsMotionBlur(node)) return;
|
|
172
|
+
blurCleanupRef.current = attachMotionBlur(node) ?? null;
|
|
173
|
+
},
|
|
174
|
+
[attachMotionBlur, detachMotionBlur],
|
|
175
|
+
);
|
|
80
176
|
|
|
81
177
|
const mergedRef = React.useCallback(
|
|
82
178
|
(node: HTMLDivElement | null) => {
|
|
83
179
|
overlayCleanupRef.current?.();
|
|
84
180
|
overlayCleanupRef.current = null;
|
|
181
|
+
detachMotionBlur();
|
|
182
|
+
hostRef.current = node;
|
|
85
183
|
if (node) {
|
|
86
184
|
const viewport = connectOverlayViewport(node);
|
|
87
185
|
const keyboard = connectKeyboardScroll(node);
|
|
88
186
|
const pointerEvents = connectBodyPointerEventsFix();
|
|
187
|
+
if (motionBlur) connectMotionBlur(node);
|
|
89
188
|
overlayCleanupRef.current = () => {
|
|
90
189
|
viewport();
|
|
91
190
|
keyboard();
|
|
@@ -94,34 +193,65 @@ const SheetContent = React.forwardRef<
|
|
|
94
193
|
}
|
|
95
194
|
assignRef(ref, node);
|
|
96
195
|
},
|
|
97
|
-
[ref],
|
|
196
|
+
[ref, motionBlur, connectMotionBlur, detachMotionBlur],
|
|
98
197
|
);
|
|
99
198
|
|
|
100
199
|
React.useLayoutEffect(() => {
|
|
101
200
|
return () => {
|
|
102
201
|
overlayCleanupRef.current?.();
|
|
103
202
|
overlayCleanupRef.current = null;
|
|
203
|
+
detachMotionBlur();
|
|
104
204
|
};
|
|
105
|
-
}, []);
|
|
205
|
+
}, [detachMotionBlur]);
|
|
206
|
+
|
|
207
|
+
// vaul may keep the same content node when only `motionBlur` flips, so the
|
|
208
|
+
// callback ref above does not always re-run. Sync attach/detach here.
|
|
209
|
+
React.useLayoutEffect(() => {
|
|
210
|
+
const node = hostRef.current;
|
|
211
|
+
if (!node) return;
|
|
212
|
+
if (motionBlur) connectMotionBlur(node);
|
|
213
|
+
else detachMotionBlur();
|
|
214
|
+
}, [motionBlur, connectMotionBlur, detachMotionBlur]);
|
|
215
|
+
|
|
216
|
+
const handle =
|
|
217
|
+
side === "bottom" || side === "top" ? (
|
|
218
|
+
<div className="flex min-h-11 shrink-0 items-center justify-center">
|
|
219
|
+
<SheetHandle className="relative mx-auto h-1 w-12 shrink-0 rounded-full bg-surface-tertiary after:absolute after:inset-x-[-16px] after:inset-y-[-20px] after:content-['']" />
|
|
220
|
+
</div>
|
|
221
|
+
) : null;
|
|
106
222
|
|
|
107
223
|
return (
|
|
108
224
|
<SheetPortal>
|
|
109
225
|
<SheetOverlay />
|
|
110
226
|
<DrawerPrimitive.Content
|
|
111
227
|
ref={mergedRef}
|
|
112
|
-
className={
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
228
|
+
className={
|
|
229
|
+
motionBlur
|
|
230
|
+
? cn(sheetBlurHostClass, sheetSideLayoutClass[side], className)
|
|
231
|
+
: cn(
|
|
232
|
+
sheetContentClass,
|
|
233
|
+
sheetSideLayoutClass[side],
|
|
234
|
+
sheetSideSurfaceClass[side],
|
|
235
|
+
className,
|
|
236
|
+
surfaceClassName,
|
|
237
|
+
)
|
|
238
|
+
}
|
|
117
239
|
{...props}
|
|
118
240
|
>
|
|
119
|
-
{
|
|
120
|
-
<div
|
|
121
|
-
|
|
241
|
+
{motionBlur ? (
|
|
242
|
+
<div
|
|
243
|
+
data-slot="sheet-surface"
|
|
244
|
+
className={cn(sheetBlurSurfaceClass, sheetSideSurfaceClass[side], surfaceClassName)}
|
|
245
|
+
>
|
|
246
|
+
{handle}
|
|
247
|
+
{children}
|
|
122
248
|
</div>
|
|
123
|
-
) :
|
|
124
|
-
|
|
249
|
+
) : (
|
|
250
|
+
<>
|
|
251
|
+
{handle}
|
|
252
|
+
{children}
|
|
253
|
+
</>
|
|
254
|
+
)}
|
|
125
255
|
</DrawerPrimitive.Content>
|
|
126
256
|
</SheetPortal>
|
|
127
257
|
);
|
|
@@ -165,3 +295,19 @@ export {
|
|
|
165
295
|
SheetHeader,
|
|
166
296
|
SheetFooter,
|
|
167
297
|
};
|
|
298
|
+
|
|
299
|
+
// The same parts under vaul's name, for apps that call the sliding panel a
|
|
300
|
+
// drawer. One implementation, so `DrawerContent` takes `motionBlur` too.
|
|
301
|
+
export {
|
|
302
|
+
Sheet as Drawer,
|
|
303
|
+
SheetPortal as DrawerPortal,
|
|
304
|
+
SheetOverlay as DrawerOverlay,
|
|
305
|
+
SheetTrigger as DrawerTrigger,
|
|
306
|
+
SheetClose as DrawerClose,
|
|
307
|
+
SheetContent as DrawerContent,
|
|
308
|
+
SheetHandle as DrawerHandle,
|
|
309
|
+
SheetTitle as DrawerTitle,
|
|
310
|
+
SheetDescription as DrawerDescription,
|
|
311
|
+
SheetHeader as DrawerHeader,
|
|
312
|
+
SheetFooter as DrawerFooter,
|
|
313
|
+
};
|
package/tokens/README.md
CHANGED
|
@@ -17,6 +17,7 @@ Design tokens and shared Tailwind v4 CSS for the vvedantb web apps. The CSS and
|
|
|
17
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
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`. From 0.15.1 the layers clip in `.t-clear-clip`, so `.t-clear` keeps the focus ring, and the placeholder layer only shows during `.is-clearing`. |
|
|
19
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`. |
|
|
20
|
+
| `@vvedantb/ui/tokens/css/type-scale.css` | `src/css/type-scale.css` | The `text-*` scale (`3xs` 10px to `7xl` 72px, with `2xs` 11px and `2sm` 13px) as `@theme` variables. Each tier has its own line height and letter-spacing: positive below 14px (`+0.01em` at 10-11px), `0` at `sm`, negative above (`-0.03em` at `7xl`). Imported by `tokens.css`. |
|
|
20
21
|
| `@vvedantb/ui/tokens/css/animations.css` | `src/css/animations.css` | `animate-aurora`, `animate-fade-in-up`, `animate-indeterminate` as `@theme` (CSS-first apps). |
|
|
21
22
|
| `@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` (both `min(var(--radius), 0.625rem)` from 0.16.3), 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
23
|
| `@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). |
|
|
@@ -74,6 +75,7 @@ Tailwind inlines an `@import` where it sits, even in the middle of a file (it mu
|
|
|
74
75
|
| `utilities.css` | `--breakpoint-sm` (Tailwind default or app theme) | Tailwind |
|
|
75
76
|
| `scrollbar-thin.css` | `--scrollbar` | tokens.css or the app theme |
|
|
76
77
|
| `clear-input.css`, `motion.css` | none (own `:root` defaults) | |
|
|
78
|
+
| `type-scale.css` | none (own `@theme` values) | |
|
|
77
79
|
| `animations.css` | none | |
|
|
78
80
|
| `tailwind-preset` | the CSS variables named in its colour map, `--radius`, `--field-radius` | tokens.css or the app theme |
|
|
79
81
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @vvedantb/ui/tokens/css/type-scale.css: the `text-*` scale with size-specific
|
|
3
|
+
* tracking. tokens.css imports it. Apps that pick files one by one can import
|
|
4
|
+
* it alone, after tailwindcss.
|
|
5
|
+
*
|
|
6
|
+
* Tracking is never one value for all sizes. Small UI text gets positive
|
|
7
|
+
* letter-spacing to stay legible. Display text gets negative letter-spacing,
|
|
8
|
+
* because letters look further apart as they grow. Line heights are the
|
|
9
|
+
* Tailwind defaults, so only tracking changes for the standard tiers.
|
|
10
|
+
*
|
|
11
|
+
* `3xs` (10px), `2xs` (11px) and `2sm` (13px) fill the gaps below `text-base`,
|
|
12
|
+
* so call sites do not need arbitrary `text-[11px]` classes.
|
|
13
|
+
*
|
|
14
|
+
* An explicit `tracking-*` or `leading-*` utility still wins. To change a tier,
|
|
15
|
+
* set its variable in the app's own `@theme` block.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
@theme {
|
|
19
|
+
--text-3xs: 0.625rem;
|
|
20
|
+
--text-3xs--line-height: 0.875rem;
|
|
21
|
+
--text-3xs--letter-spacing: 0.01em;
|
|
22
|
+
--text-2xs: 0.6875rem;
|
|
23
|
+
--text-2xs--line-height: 1rem;
|
|
24
|
+
--text-2xs--letter-spacing: 0.01em;
|
|
25
|
+
--text-xs: 0.75rem;
|
|
26
|
+
--text-xs--line-height: 1rem;
|
|
27
|
+
--text-xs--letter-spacing: 0.005em;
|
|
28
|
+
--text-2sm: 0.8125rem;
|
|
29
|
+
--text-2sm--line-height: 1.125rem;
|
|
30
|
+
--text-2sm--letter-spacing: 0.005em;
|
|
31
|
+
--text-sm: 0.875rem;
|
|
32
|
+
--text-sm--line-height: 1.25rem;
|
|
33
|
+
--text-sm--letter-spacing: 0em;
|
|
34
|
+
--text-base: 1rem;
|
|
35
|
+
--text-base--line-height: 1.5rem;
|
|
36
|
+
--text-base--letter-spacing: -0.006em;
|
|
37
|
+
--text-lg: 1.125rem;
|
|
38
|
+
--text-lg--line-height: 1.75rem;
|
|
39
|
+
--text-lg--letter-spacing: -0.01em;
|
|
40
|
+
--text-xl: 1.25rem;
|
|
41
|
+
--text-xl--line-height: 1.75rem;
|
|
42
|
+
--text-xl--letter-spacing: -0.012em;
|
|
43
|
+
--text-2xl: 1.5rem;
|
|
44
|
+
--text-2xl--line-height: 2rem;
|
|
45
|
+
--text-2xl--letter-spacing: -0.015em;
|
|
46
|
+
--text-3xl: 1.875rem;
|
|
47
|
+
--text-3xl--line-height: 2.25rem;
|
|
48
|
+
--text-3xl--letter-spacing: -0.018em;
|
|
49
|
+
--text-4xl: 2.25rem;
|
|
50
|
+
--text-4xl--line-height: 2.5rem;
|
|
51
|
+
--text-4xl--letter-spacing: -0.022em;
|
|
52
|
+
--text-5xl: 3rem;
|
|
53
|
+
--text-5xl--line-height: 1;
|
|
54
|
+
--text-5xl--letter-spacing: -0.025em;
|
|
55
|
+
--text-6xl: 3.75rem;
|
|
56
|
+
--text-6xl--line-height: 1;
|
|
57
|
+
--text-6xl--letter-spacing: -0.028em;
|
|
58
|
+
--text-7xl: 4.5rem;
|
|
59
|
+
--text-7xl--line-height: 1;
|
|
60
|
+
--text-7xl--letter-spacing: -0.03em;
|
|
61
|
+
}
|
package/tokens/src/tokens.css
CHANGED
|
@@ -23,11 +23,15 @@
|
|
|
23
23
|
* modal / tabs motion variables. Every rule sits in a Tailwind layer and the
|
|
24
24
|
* timings and strengths are `:root` variables, so app rules and app `:root`
|
|
25
25
|
* values win.
|
|
26
|
+
*
|
|
27
|
+
* It also brings the `text-*` type scale with size-specific
|
|
28
|
+
* tracking (css/type-scale.css), including the `3xs`, `2xs` and `2sm` tiers.
|
|
26
29
|
*/
|
|
27
30
|
|
|
28
31
|
@import "./css/clear-input.css";
|
|
29
32
|
@import "./css/scrollbar-thin.css";
|
|
30
33
|
@import "./css/motion.css";
|
|
34
|
+
@import "./css/type-scale.css";
|
|
31
35
|
|
|
32
36
|
@custom-variant dark (&:where(.dark, .dark *));
|
|
33
37
|
|