@lessly/ui 1.3.0 → 1.5.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 CHANGED
@@ -6,7 +6,8 @@ Module Federation host provides to federation extension remotes at runtime, and
6
6
  it is published to the public npm registry (npmjs.com).
7
7
 
8
8
  > **Contract:** the package name (`@lessly/ui`), its exports (`.`,
9
- > `./tailwind-preset`, `./styles.css`), and its peer dependencies (React 19,
9
+ > `./tailwind-preset`, `./styles.css`, `./styles-federated.css`), and its peer
10
+ > dependencies (React 19,
10
11
  > react-router 7, optional `tailwindcss >=3.4`) are a cross-project contract. Do
11
12
  > not change them without coordinating the workspace host and every federation
12
13
  > remote.
@@ -24,7 +25,10 @@ it is published to the public npm registry (npmjs.com).
24
25
  upstream in `lessly-hub/design-system`) with `darkMode: 'class'` added, and
25
26
  `styles.css` bundles the package's `theme.css` (CSS variables, `.light`
26
27
  overrides, typography classes) plus this library's fonts and base layer.
27
- Token values are never edited here they change by bumping `@lessly/tokens`.
28
+ `styles-federated.css` is the same sheet without the `@font-face` rules and
29
+ the `@layer base` block, for remotes that must not restyle the page they load
30
+ into. Token values are never edited here — they change by bumping
31
+ `@lessly/tokens`.
28
32
 
29
33
  ## Install
30
34
 
@@ -42,9 +46,46 @@ import { Button, useTheme } from '@lessly/ui';
42
46
  import '@lessly/ui/styles.css';
43
47
  ```
44
48
 
45
- Your app's Tailwind config must use the preset so token utilities resolve. The
46
- preset needs Tailwind CSS >= 3.4 (declared as an optional peer dependency
47
- only required if you consume the preset):
49
+ Tailwind is what turns the kit's class names into CSS. The package ships **no
50
+ compiled utility CSS** every consumer runs its own Tailwind over the token
51
+ preset (`tailwindcss >= 3.4`, an optional peer dependency, required only if you
52
+ consume the preset).
53
+
54
+ ### Tailwind v4 (CSS-first)
55
+
56
+ Everything is declared in your CSS entry:
57
+
58
+ ```css
59
+ @import 'tailwindcss';
60
+ /* The token preset. v4 has no `presets:` key of its own, so the preset is
61
+ loaded from a JS/TS config file — without this, `bg-bg-surface`,
62
+ `text-text-primary` and `gap-gutter` emit nothing at all. */
63
+ @config './tailwind.config.ts';
64
+ /* Token variables, `.light` overrides, fonts, base layer. */
65
+ @import '@lessly/ui/styles.css';
66
+ /* Scan the library. v4 does not scan `node_modules` on its own, and the kit's
67
+ components (Grid, Col, …) carry literal utilities (`md:col-span-5`,
68
+ `gap-gutter`) as strings inside `dist/index.js`. The glob matters: a bare
69
+ directory path is skipped, because Tailwind honours your `.gitignore` and
70
+ `node_modules/` is in it. */
71
+ @source './node_modules/@lessly/ui/dist/**/*.js';
72
+ ```
73
+
74
+ with the config file next to it holding nothing but the preset:
75
+
76
+ ```ts
77
+ import type { Config } from 'tailwindcss';
78
+ import { lesslyPreset } from '@lessly/ui/tailwind-preset';
79
+ export default { presets: [lesslyPreset] } satisfies Config;
80
+ ```
81
+
82
+ Paths in `@source` and `@config` are relative to the CSS file. If your CSS entry
83
+ sits in `src/`, they read `'../tailwind.config.ts'` and
84
+ `'../node_modules/@lessly/ui/dist/**/*.js'`.
85
+
86
+ ### Tailwind v3
87
+
88
+ The preset and the library scan both go in the JS config:
48
89
 
49
90
  ```ts
50
91
  import { lesslyPreset } from '@lessly/ui/tailwind-preset';
@@ -61,6 +102,21 @@ CSS variables use the upstream unprefixed names (`--bg-primary`,
61
102
  deprecated alias layer in `styles.css` and will be removed in the next major —
62
103
  migrate any direct `var(--color-*)` reads.
63
104
 
105
+ ### Federated remotes
106
+
107
+ A Module Federation remote loads its CSS after the shell has painted, so it must
108
+ not restyle the live page: `@font-face` rules and a `@layer base` body rule
109
+ re-applied on top of a rendered page shift font metrics on every cold load.
110
+ Import the federation-safe sheet instead — same tokens, aliases, ui variables
111
+ and animation layer, without those two blocks:
112
+
113
+ ```ts
114
+ import '@lessly/ui/styles-federated.css';
115
+ ```
116
+
117
+ The shell keeps importing `@lessly/ui/styles.css`; it owns the fonts and the
118
+ base layer.
119
+
64
120
  ## Storybook
65
121
 
66
122
  ```bash
@@ -86,7 +142,7 @@ npm package published to npmjs always contains the package, never the site or St
86
142
 
87
143
  | Script | Description |
88
144
  | --- | --- |
89
- | `pnpm build` | tsup package build → `dist/` (+ assembles `styles.css` from the @lessly/tokens theme, deprecated `--color-*` aliases, ui-local pieces, and the animation layer) — the npm package published to npmjs |
145
+ | `pnpm build` | tsup package build → `dist/` (+ assembles `styles.css` from the @lessly/tokens theme, deprecated `--color-*` aliases, ui-local pieces, and the animation layer, and the federation-safe `styles-federated.css` beside it) — the npm package published to npmjs |
90
146
  | `pnpm build-storybook` | Storybook static site → `storybook-static/` — served at `ui.lessly.com/storybook` by `site/server.mjs` |
91
147
  | `pnpm build-site` | Landing app (`site/`) → `site/dist/` — served at `ui.lessly.com/` by `site/server.mjs` |
92
148
  | `pnpm storybook` | Storybook dev server on `http://localhost:6006` |
package/dist/index.d.ts CHANGED
@@ -128,6 +128,11 @@ interface CardProps extends Omit<React$1.HTMLAttributes<HTMLDivElement>, 'title'
128
128
  * action no bar is drawn at all, so a list starts at the top of the card.
129
129
  */
130
130
  title?: React$1.ReactNode;
131
+ /**
132
+ * The native HTML tooltip on the card's box, shown by the browser on hover. `title` is the
133
+ * visible heading here, so this is the only way to set the attribute.
134
+ */
135
+ tooltip?: string;
131
136
  action?: React$1.ReactNode;
132
137
  /**
133
138
  * `list` clips to the card's radius and hairlines between children.
package/dist/index.js CHANGED
@@ -204,10 +204,11 @@ Button.displayName = "Button";
204
204
  import * as React3 from "react";
205
205
  import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
206
206
  var Card = React3.forwardRef(
207
- ({ className, title, action, variant = "default", children, ...props }, ref) => /* @__PURE__ */ jsxs2(
207
+ ({ className, title, tooltip, action, variant = "default", children, ...props }, ref) => /* @__PURE__ */ jsxs2(
208
208
  "div",
209
209
  {
210
210
  ref,
211
+ title: tooltip,
211
212
  className: cn(
212
213
  "rounded-xl border border-border-subtle bg-bg-surface text-text-primary shadow-sm",
213
214
  variant === "list" && "overflow-hidden",
@@ -3886,21 +3887,21 @@ var ImageCropper = React57.forwardRef(
3886
3887
  React57.useLayoutEffect(() => {
3887
3888
  const el = stageRef.current;
3888
3889
  if (!el) return void 0;
3889
- const read = () => {
3890
- const rect = el.getBoundingClientRect();
3890
+ const read = (entry) => {
3891
3891
  const style = typeof getComputedStyle === "function" ? getComputedStyle(el) : null;
3892
3892
  const edge = (value) => value ? parseFloat(value) || 0 : 0;
3893
3893
  const bl = edge(style?.borderLeftWidth);
3894
3894
  const bt = edge(style?.borderTopWidth);
3895
3895
  inset.current = { left: bl, top: bt };
3896
- const w = rect.width - bl - edge(style?.borderRightWidth);
3897
- const h = rect.height - bt - edge(style?.borderBottomWidth);
3896
+ const border = entry?.borderBoxSize?.[0];
3897
+ const w = border ? border.inlineSize - bl - edge(style?.borderRightWidth) : el.clientWidth;
3898
+ const h = border ? border.blockSize - bt - edge(style?.borderBottomWidth) : el.clientHeight;
3898
3899
  if (w <= 0 || h <= 0) return;
3899
3900
  setStage((prev) => prev.w === w && prev.h === h ? prev : { w, h });
3900
3901
  };
3901
3902
  read();
3902
3903
  if (typeof ResizeObserver === "undefined") return void 0;
3903
- const observer = new ResizeObserver(read);
3904
+ const observer = new ResizeObserver((entries) => read(entries[0]));
3904
3905
  observer.observe(el);
3905
3906
  return () => observer.disconnect();
3906
3907
  }, []);
@@ -5107,13 +5108,13 @@ function ItemLabel({ item, kind }) {
5107
5108
  const name = nameOf(item);
5108
5109
  return /* @__PURE__ */ jsx85("span", { className: cn("min-w-0 flex-1 truncate", !name && "text-text-tertiary"), children: name ?? UNNAMED[kind] });
5109
5110
  }
5110
- function Mark2({ item, size }) {
5111
+ function Mark2({ item, size, reserve }) {
5111
5112
  const box = size === "md" ? "size-7" : "size-5";
5112
5113
  if (item.icon) {
5113
5114
  return /* @__PURE__ */ jsx85("span", { className: cn("flex shrink-0 items-center justify-center overflow-hidden rounded-md", box), "aria-hidden": "true", children: item.icon });
5114
5115
  }
5115
5116
  const initial = nameOf(item)?.trim().charAt(0).toUpperCase();
5116
- if (!initial) return null;
5117
+ if (!initial) return reserve ? /* @__PURE__ */ jsx85("span", { "aria-hidden": "true", className: cn("shrink-0", box) }) : null;
5117
5118
  return /* @__PURE__ */ jsx85(
5118
5119
  "span",
5119
5120
  {
@@ -5172,7 +5173,7 @@ function OrgProductSwitcher({
5172
5173
  children: [
5173
5174
  /* @__PURE__ */ jsx85(DropdownMenuLabel, { className: sectionLabel, children: "Organizations" }),
5174
5175
  organizations.map((o) => /* @__PURE__ */ jsxs56(DropdownMenuItem, { className: menuItem, onSelect: () => onOrgSelect(o.id), children: [
5175
- /* @__PURE__ */ jsx85(Mark2, { item: o, size: "sm" }),
5176
+ /* @__PURE__ */ jsx85(Mark2, { item: o, size: "sm", reserve: true }),
5176
5177
  /* @__PURE__ */ jsx85(ItemLabel, { item: o, kind: "organization" }),
5177
5178
  o.id === activeOrgId && /* @__PURE__ */ jsx85(Check9, { className: "size-4 shrink-0 text-text-secondary", "aria-hidden": "true" })
5178
5179
  ] }, o.id)),
@@ -5180,7 +5181,7 @@ function OrgProductSwitcher({
5180
5181
  /* @__PURE__ */ jsx85(DropdownMenuSeparator, { className: "mx-0 shrink-0 bg-border-subtle" }),
5181
5182
  /* @__PURE__ */ jsx85(DropdownMenuLabel, { className: sectionLabel, children: orgName ? `${orgName} \xB7 Products` : "Products" }),
5182
5183
  products.map((p) => /* @__PURE__ */ jsxs56(DropdownMenuItem, { className: menuItem, onSelect: () => onProductSelect?.(p.id), children: [
5183
- /* @__PURE__ */ jsx85(Mark2, { item: p, size: "sm" }),
5184
+ /* @__PURE__ */ jsx85(Mark2, { item: p, size: "sm", reserve: true }),
5184
5185
  /* @__PURE__ */ jsx85(ItemLabel, { item: p, kind: "product" }),
5185
5186
  p.id === activeProductId && /* @__PURE__ */ jsx85(Check9, { className: "size-4 shrink-0 text-text-secondary", "aria-hidden": "true" })
5186
5187
  ] }, p.id))
@@ -0,0 +1,937 @@
1
+ /* === @lessly/ui styles, federation-safe — generated by scripts/build-styles.mjs === */
2
+ /* dist/styles.css without its font declarations and its base layer: a Module
3
+ Federation remote loads after the shell has painted and must not restyle the
4
+ live page (FED-008). Everything else is identical. */
5
+
6
+ /*
7
+ * Auto-generated by tokens/build.js — do not edit manually.
8
+ * Source of truth: tokens/primitives.json + tokens/semantic.json.
9
+ * Run: node tokens/build.js
10
+ *
11
+ * Dark is the default theme (:root). Light activates via a `light` class
12
+ * on a parent element (usually <html> or <body>).
13
+ */
14
+
15
+ :root {
16
+ --bg-primary: #191b24;
17
+ --bg-secondary: #12141b;
18
+ --bg-surface: #1f222d;
19
+ --bg-elevated: #252936;
20
+ --bg-overlay: #252936;
21
+ --bg-sunken: #0e1015;
22
+ --bg-selected: #1f222d;
23
+ --bg-brand: #06297d;
24
+ --bg-brand-subtle: #1f2541;
25
+ --bg-brand-hover: #08349b;
26
+ --bg-brand-bright: #165ff2;
27
+ --bg-brand-bright-hover: #3d7af4;
28
+ --bg-success: #13703c;
29
+ --bg-success-bright: #21be63;
30
+ --bg-success-subtle: #0a1911;
31
+ --bg-warning: #b3780e;
32
+ --bg-warning-subtle: #1a1508;
33
+ --bg-danger: #78100b;
34
+ --bg-danger-subtle: #1a0a09;
35
+ --bg-teal-subtle: #091918;
36
+ --bg-violet-subtle: #12091a;
37
+ --bg-rose-subtle: #1a090b;
38
+ --text-primary: #eeeff0;
39
+ --text-secondary: #a5a9b5;
40
+ --text-tertiary: #8a93a8;
41
+ --text-disabled: #60687f;
42
+ --text-inverse: #0e1015;
43
+ --text-on-brand: #f6f7f7;
44
+ --text-on-success: #f6f7f7;
45
+ --text-on-success-bright: #08090c;
46
+ --text-on-warning: #08090c;
47
+ --text-on-danger: #f6f7f7;
48
+ --text-brand: #6394f6;
49
+ --text-success: #2eda76;
50
+ --text-warning: #efb042;
51
+ --text-danger: #f06a6a;
52
+ --text-link: #6394f6;
53
+ --text-link-hover: #a3b7e0;
54
+ --text-teal: #70eada;
55
+ --text-violet: #ad6cee;
56
+ --text-rose: #f06a85;
57
+ --icon-primary: #cfd1d7;
58
+ --icon-secondary: #737c95;
59
+ --icon-disabled: #393d4b;
60
+ --icon-brand: #6394f6;
61
+ --icon-success: #2eda76;
62
+ --icon-warning: #efb042;
63
+ --icon-danger: #f06a6a;
64
+ --border-subtle: #2a2e3b;
65
+ --border-default: #535a6e;
66
+ --border-strong: #8a91a7;
67
+ --border-selected: #737c95;
68
+ --border-brand: #165ff2;
69
+ --border-focus: #3d7af4;
70
+ --border-success: #1da456;
71
+ --border-warning: #b3780e;
72
+ --border-danger: #e82020;
73
+ --field-bg: #0e1015;
74
+ --field-bg-hover: #12141b;
75
+ --field-bg-focus: #0e1015;
76
+ --field-bg-valid: #191b24;
77
+ --field-border: #60687f;
78
+ --field-border-hover: #737c95;
79
+ --field-border-focus: #3d7af4;
80
+ --field-border-filled: #737c95;
81
+ --field-border-error: #e82020;
82
+ --field-border-success: #188a49;
83
+ --field-ring-focus: rgba(61,122,244,0.25);
84
+ --field-ring-error: rgba(232,32,32,0.2);
85
+ --field-ring-success: rgba(24,138,73,0.15);
86
+ --shadow-sm: 0 1px 2px rgba(0,0,0,0.4);
87
+ --shadow-md: 0 0 0 1px rgba(255,255,255,0.18), 0 2px 8px rgba(0,0,0,0.5), 0 1px 2px rgba(0,0,0,0.4);
88
+ --shadow-lg: 0 0 0 1px rgba(255,255,255,0.18), 0 8px 24px rgba(0,0,0,0.6), 0 2px 8px rgba(0,0,0,0.4);
89
+ --shadow-xl: 0 0 0 1px rgba(255,255,255,0.18), 0 16px 48px rgba(0,0,0,0.7), 0 4px 12px rgba(0,0,0,0.5);
90
+ --shadow-overlay: 0 0 0 1px rgba(255,255,255,0.18), 0 8px 24px rgba(0,0,0,0.5);
91
+ --shadow-inner: inset 0 1px 3px rgba(0,0,0,0.4);
92
+ --shadow-glow-brand: 0 0 12px rgba(6,41,125,0.5);
93
+ --overlay-backdrop: rgba(8,9,12,0.80);
94
+ --overlay-scrim: rgba(8,9,12,0.60);
95
+ --overlay-modal-bg: #1f222d;
96
+ --focus-ring-color: #3d7af4;
97
+ --focus-ring-offset: 2px;
98
+ --focus-ring-width: 2px;
99
+ --grid-gutter: 16px;
100
+ --grid-margin-sm: 16px;
101
+ --grid-margin-md: 24px;
102
+ --grid-margin-xl: 40px;
103
+ --grid-container-max: 1440px;
104
+
105
+ /* typography text-styles — theme-invariant, :root only (see typographyVars in build.js) */
106
+ --text-display-xl-font-family: "Instrument Serif", Georgia, "Times New Roman", serif;
107
+ --text-display-xl-font-size: 3.5rem;
108
+ --text-display-xl-line-height: 1.05;
109
+ --text-display-xl-font-weight: 400;
110
+ --text-display-xl-letter-spacing: -0.02em;
111
+ --text-display-lg-font-family: "Instrument Serif", Georgia, "Times New Roman", serif;
112
+ --text-display-lg-font-size: 3rem;
113
+ --text-display-lg-line-height: 1.1;
114
+ --text-display-lg-font-weight: 400;
115
+ --text-display-lg-letter-spacing: -0.02em;
116
+ --text-h1-font-family: "Inter", system-ui, -apple-system, sans-serif;
117
+ --text-h1-font-size: 2.4375rem;
118
+ --text-h1-line-height: 1.15;
119
+ --text-h1-font-weight: 800;
120
+ --text-h1-letter-spacing: -0.025em;
121
+ --text-h2-font-family: "Inter", system-ui, -apple-system, sans-serif;
122
+ --text-h2-font-size: 2.0625rem;
123
+ --text-h2-line-height: 1.2;
124
+ --text-h2-font-weight: 800;
125
+ --text-h2-letter-spacing: -0.02em;
126
+ --text-h3-font-family: "Inter", system-ui, -apple-system, sans-serif;
127
+ --text-h3-font-size: 1.6875rem;
128
+ --text-h3-line-height: 1.25;
129
+ --text-h3-font-weight: 800;
130
+ --text-h3-letter-spacing: -0.015em;
131
+ --text-h4-font-family: "Inter", system-ui, -apple-system, sans-serif;
132
+ --text-h4-font-size: 1.4375rem;
133
+ --text-h4-line-height: 1.3;
134
+ --text-h4-font-weight: 800;
135
+ --text-h4-letter-spacing: -0.01em;
136
+ --text-h5-font-family: "Inter", system-ui, -apple-system, sans-serif;
137
+ --text-h5-font-size: 1.1875rem;
138
+ --text-h5-line-height: 1.35;
139
+ --text-h5-font-weight: 800;
140
+ --text-h5-letter-spacing: -0.005em;
141
+ --text-h6-font-family: "Inter", system-ui, -apple-system, sans-serif;
142
+ --text-h6-font-size: 1rem;
143
+ --text-h6-line-height: 1.4;
144
+ --text-h6-font-weight: 800;
145
+ --text-h6-letter-spacing: 0;
146
+ --text-body-lg-font-family: "Inter", system-ui, -apple-system, sans-serif;
147
+ --text-body-lg-font-size: 1.125rem;
148
+ --text-body-lg-line-height: 1.6;
149
+ --text-body-lg-font-weight: 400;
150
+ --text-body-lg-letter-spacing: 0;
151
+ --text-body-font-family: "Inter", system-ui, -apple-system, sans-serif;
152
+ --text-body-font-size: 1rem;
153
+ --text-body-line-height: 1.6;
154
+ --text-body-font-weight: 400;
155
+ --text-body-letter-spacing: 0;
156
+ --text-body-sm-font-family: "Inter", system-ui, -apple-system, sans-serif;
157
+ --text-body-sm-font-size: 0.875rem;
158
+ --text-body-sm-line-height: 1.5;
159
+ --text-body-sm-font-weight: 400;
160
+ --text-body-sm-letter-spacing: 0;
161
+ --text-caption-font-family: "Inter", system-ui, -apple-system, sans-serif;
162
+ --text-caption-font-size: 0.75rem;
163
+ --text-caption-line-height: 1.4;
164
+ --text-caption-font-weight: 400;
165
+ --text-caption-letter-spacing: 0;
166
+ --text-overline-font-family: "Inter", system-ui, -apple-system, sans-serif;
167
+ --text-overline-font-size: 0.6875rem;
168
+ --text-overline-line-height: 1.4;
169
+ --text-overline-font-weight: 600;
170
+ --text-overline-letter-spacing: 0.06em;
171
+ --text-overline-text-transform: uppercase;
172
+ --text-label-lg-font-family: "Inter", system-ui, -apple-system, sans-serif;
173
+ --text-label-lg-font-size: 1rem;
174
+ --text-label-lg-line-height: 1.4;
175
+ --text-label-lg-font-weight: 500;
176
+ --text-label-lg-letter-spacing: 0;
177
+ --text-label-md-font-family: "Inter", system-ui, -apple-system, sans-serif;
178
+ --text-label-md-font-size: 0.875rem;
179
+ --text-label-md-line-height: 1.4;
180
+ --text-label-md-font-weight: 500;
181
+ --text-label-md-letter-spacing: 0;
182
+ --text-label-sm-font-family: "Inter", system-ui, -apple-system, sans-serif;
183
+ --text-label-sm-font-size: 0.75rem;
184
+ --text-label-sm-line-height: 1.4;
185
+ --text-label-sm-font-weight: 500;
186
+ --text-label-sm-letter-spacing: 0;
187
+ --text-button-lg-font-family: "Inter", system-ui, -apple-system, sans-serif;
188
+ --text-button-lg-font-size: 1rem;
189
+ --text-button-lg-line-height: 1;
190
+ --text-button-lg-font-weight: 600;
191
+ --text-button-lg-letter-spacing: 0;
192
+ --text-button-md-font-family: "Inter", system-ui, -apple-system, sans-serif;
193
+ --text-button-md-font-size: 0.8125rem;
194
+ --text-button-md-line-height: 1;
195
+ --text-button-md-font-weight: 600;
196
+ --text-button-md-letter-spacing: 0;
197
+ --text-button-sm-font-family: "Inter", system-ui, -apple-system, sans-serif;
198
+ --text-button-sm-font-size: 0.6875rem;
199
+ --text-button-sm-line-height: 1;
200
+ --text-button-sm-font-weight: 600;
201
+ --text-button-sm-letter-spacing: 0;
202
+ --text-code-lg-font-family: "Fira Mono", "Fira Code", "Cascadia Code", monospace;
203
+ --text-code-lg-font-size: 1rem;
204
+ --text-code-lg-line-height: 1.6;
205
+ --text-code-lg-font-weight: 400;
206
+ --text-code-lg-letter-spacing: 0;
207
+ --text-code-font-family: "Fira Mono", "Fira Code", "Cascadia Code", monospace;
208
+ --text-code-font-size: 0.875rem;
209
+ --text-code-line-height: 1.5;
210
+ --text-code-font-weight: 400;
211
+ --text-code-letter-spacing: 0;
212
+ --text-code-sm-font-family: "Fira Mono", "Fira Code", "Cascadia Code", monospace;
213
+ --text-code-sm-font-size: 0.75rem;
214
+ --text-code-sm-line-height: 1.4;
215
+ --text-code-sm-font-weight: 400;
216
+ --text-code-sm-letter-spacing: 0;
217
+
218
+ /* layout — large dimensions (160–384px) for section spacing, theme-invariant (see layoutVars in build.js) */
219
+ --layout-40: 160px;
220
+ --layout-48: 192px;
221
+ --layout-56: 224px;
222
+ --layout-64: 256px;
223
+ --layout-80: 320px;
224
+ --layout-96: 384px;
225
+
226
+ /* motion — primitive + semantic duration/easing, theme-invariant (see motionVars in build.js) */
227
+ --motion-duration-instant: 0ms;
228
+ --motion-duration-fast: 100ms;
229
+ --motion-duration-normal: 200ms;
230
+ --motion-duration-slow: 300ms;
231
+ --motion-duration-slower: 500ms;
232
+ --motion-duration-slowest: 800ms;
233
+ --motion-easing-default: cubic-bezier(0.25, 0.1, 0.25, 1);
234
+ --motion-easing-in: cubic-bezier(0.42, 0, 1, 1);
235
+ --motion-easing-out: cubic-bezier(0, 0, 0.58, 1);
236
+ --motion-easing-in-out: cubic-bezier(0.42, 0, 0.58, 1);
237
+ --motion-easing-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
238
+ --motion-easing-linear: cubic-bezier(0, 0, 1, 1);
239
+ --motion-duration-ui: 100ms;
240
+ --motion-duration-collapse: 100ms;
241
+ --motion-duration-slide: 200ms;
242
+ --motion-duration-overlay: 300ms;
243
+ --motion-duration-toast: 300ms;
244
+ --motion-easing-standard: cubic-bezier(0.25, 0.1, 0.25, 1);
245
+ --motion-easing-enter: cubic-bezier(0, 0, 0.58, 1);
246
+ --motion-easing-exit: cubic-bezier(0.42, 0, 1, 1);
247
+ --motion-easing-emphasized: cubic-bezier(0.34, 1.56, 0.64, 1);
248
+
249
+ /* shadcn/ui role bridge — aliases to the Lessly tokens above (see SHADCN_BRIDGE
250
+ in build.js). Re-declared under .light below so a nested .light re-themes too. */
251
+ --background: var(--bg-primary);
252
+ --foreground: var(--text-primary);
253
+ --card: var(--bg-surface);
254
+ --card-foreground: var(--text-primary);
255
+ --popover: var(--bg-overlay);
256
+ --popover-foreground: var(--text-primary);
257
+ --primary: var(--bg-brand-bright);
258
+ --primary-foreground: var(--text-on-brand);
259
+ --secondary: var(--bg-elevated);
260
+ --secondary-foreground: var(--text-primary);
261
+ --muted: var(--bg-sunken);
262
+ --muted-foreground: var(--text-tertiary);
263
+ --accent: var(--bg-elevated);
264
+ --accent-foreground: var(--text-primary);
265
+ --destructive: var(--bg-danger);
266
+ --destructive-foreground: var(--text-on-danger);
267
+ --border: var(--border-default);
268
+ --input: var(--field-border);
269
+ --ring: var(--focus-ring-color);
270
+ --success: var(--bg-success);
271
+ --success-foreground: var(--text-on-success);
272
+ --warning: var(--bg-warning);
273
+ --warning-foreground: var(--text-on-warning);
274
+ --link: var(--text-link);
275
+ --link-hover: var(--text-link-hover);
276
+ --inverted-foreground: var(--bg-brand);
277
+ }
278
+
279
+ .light {
280
+ --bg-primary: #f6f7f7;
281
+ --bg-secondary: #eeeff0;
282
+ --bg-surface: #f2f3f4;
283
+ --bg-elevated: #edeef0;
284
+ --bg-overlay: #ffffff;
285
+ --bg-sunken: #e1e2e4;
286
+ --bg-selected: #f2f3f4;
287
+ --bg-brand-subtle: #e4ecfe;
288
+ --bg-brand-hover: #05226a;
289
+ --bg-brand-bright-hover: #0b4cd5;
290
+ --bg-success-subtle: #ebf3ef;
291
+ --bg-warning: #eca01c;
292
+ --bg-warning-subtle: #f4f0eb;
293
+ --bg-danger-subtle: #f4ebeb;
294
+ --bg-teal-subtle: #ebf3f2;
295
+ --bg-violet-subtle: #efebf3;
296
+ --bg-rose-subtle: #f4ebed;
297
+ --text-primary: #0e1015;
298
+ --text-secondary: #393d4b;
299
+ --text-tertiary: #535a6e;
300
+ --text-disabled: #737c95;
301
+ --text-inverse: #f6f7f7;
302
+ --text-brand: #0b4cd5;
303
+ --text-success: #13703c;
304
+ --text-warning: #7a5409;
305
+ --text-danger: #94120e;
306
+ --text-link: #0940b8;
307
+ --text-link-hover: #0b4cd5;
308
+ --text-teal: #107469;
309
+ --text-violet: #450d77;
310
+ --text-rose: #780b1f;
311
+ --icon-primary: #252936;
312
+ --icon-secondary: #535a6e;
313
+ --icon-disabled: #8a91a7;
314
+ --icon-brand: #165ff2;
315
+ --icon-success: #13703c;
316
+ --icon-warning: #7a5409;
317
+ --icon-danger: #94120e;
318
+ --border-subtle: #cfd1d7;
319
+ --border-default: #bbbec7;
320
+ --border-strong: #60687f;
321
+ --border-focus: #0940b8;
322
+ --border-success: #188a49;
323
+ --field-bg: #ffffff;
324
+ --field-bg-hover: #f6f7f7;
325
+ --field-bg-focus: #ffffff;
326
+ --field-bg-valid: #eeeff0;
327
+ --field-border: #a5a9b5;
328
+ --field-border-focus: #165ff2;
329
+ --field-border-success: #13703c;
330
+ --field-ring-focus: rgba(22,95,242,0.15);
331
+ --field-ring-error: rgba(232,32,32,0.12);
332
+ --field-ring-success: rgba(19,112,60,0.1);
333
+ --shadow-sm: 0 1px 2px rgba(0,0,0,0.06);
334
+ --shadow-md: 0 2px 8px rgba(0,0,0,0.08), 0 1px 2px rgba(0,0,0,0.04);
335
+ --shadow-lg: 0 8px 24px rgba(0,0,0,0.12), 0 2px 8px rgba(0,0,0,0.06);
336
+ --shadow-xl: 0 16px 48px rgba(0,0,0,0.16), 0 4px 12px rgba(0,0,0,0.08);
337
+ --shadow-overlay: 0 12px 32px rgba(16,24,40,0.1), 0 2px 8px rgba(16,24,40,0.06);
338
+ --shadow-inner: inset 0 1px 3px rgba(0,0,0,0.08);
339
+ --shadow-glow-brand: 0 0 12px rgba(6,41,125,0.2);
340
+ --overlay-backdrop: rgba(14,16,21,0.50);
341
+ --overlay-scrim: rgba(14,16,21,0.30);
342
+ --overlay-modal-bg: #ffffff;
343
+ --focus-ring-color: #0940b8;
344
+
345
+ /* shadcn role bridge re-declared so .light on a nested element (not just <html>)
346
+ re-resolves the aliases to light values. */
347
+ --background: var(--bg-primary);
348
+ --foreground: var(--text-primary);
349
+ --card: var(--bg-surface);
350
+ --card-foreground: var(--text-primary);
351
+ --popover: var(--bg-overlay);
352
+ --popover-foreground: var(--text-primary);
353
+ --primary: var(--bg-brand-bright);
354
+ --primary-foreground: var(--text-on-brand);
355
+ --secondary: var(--bg-elevated);
356
+ --secondary-foreground: var(--text-primary);
357
+ --muted: var(--bg-sunken);
358
+ --muted-foreground: var(--text-tertiary);
359
+ --accent: var(--bg-elevated);
360
+ --accent-foreground: var(--text-primary);
361
+ --destructive: var(--bg-danger);
362
+ --destructive-foreground: var(--text-on-danger);
363
+ --border: var(--border-default);
364
+ --input: var(--field-border);
365
+ --ring: var(--focus-ring-color);
366
+ --success: var(--bg-success);
367
+ --success-foreground: var(--text-on-success);
368
+ --warning: var(--bg-warning);
369
+ --warning-foreground: var(--text-on-warning);
370
+ --link: var(--text-link);
371
+ --link-hover: var(--text-link-hover);
372
+ --inverted-foreground: var(--bg-brand);
373
+ }
374
+
375
+ /* Link utility — the composite .text-<style> classes generated here are stripped by scripts/build-styles.mjs; their --text-* variables above are untouched. */
376
+ .link {
377
+ color: var(--text-link);
378
+ text-decoration: underline;
379
+ text-underline-offset: 2px;
380
+ }
381
+
382
+ .link:hover {
383
+ color: var(--text-link-hover);
384
+ }
385
+
386
+ /* Reduced motion — collapse every motion variable to instant/linear (see reducedMotionBlock in build.js). */
387
+ @media (prefers-reduced-motion: reduce) {
388
+ :root {
389
+ --motion-duration-instant: 0ms;
390
+ --motion-duration-fast: 0ms;
391
+ --motion-duration-normal: 0ms;
392
+ --motion-duration-slow: 0ms;
393
+ --motion-duration-slower: 0ms;
394
+ --motion-duration-slowest: 0ms;
395
+ --motion-duration-ui: 0ms;
396
+ --motion-duration-collapse: 0ms;
397
+ --motion-duration-slide: 0ms;
398
+ --motion-duration-overlay: 0ms;
399
+ --motion-duration-toast: 0ms;
400
+ --motion-easing-default: cubic-bezier(0, 0, 1, 1);
401
+ --motion-easing-in: cubic-bezier(0, 0, 1, 1);
402
+ --motion-easing-out: cubic-bezier(0, 0, 1, 1);
403
+ --motion-easing-in-out: cubic-bezier(0, 0, 1, 1);
404
+ --motion-easing-spring: cubic-bezier(0, 0, 1, 1);
405
+ --motion-easing-linear: cubic-bezier(0, 0, 1, 1);
406
+ --motion-easing-standard: cubic-bezier(0, 0, 1, 1);
407
+ --motion-easing-enter: cubic-bezier(0, 0, 1, 1);
408
+ --motion-easing-exit: cubic-bezier(0, 0, 1, 1);
409
+ --motion-easing-emphasized: cubic-bezier(0, 0, 1, 1);
410
+ }
411
+ }
412
+
413
+ /* === Deprecated aliases — remove in the next major ===
414
+ Old vendored `--color-*` names mapped onto the @lessly/tokens variables above.
415
+ Generated by scripts/build-styles.mjs — do not edit. */
416
+ :root {
417
+ --color-bg-primary: var(--bg-primary);
418
+ --color-bg-secondary: var(--bg-secondary);
419
+ --color-bg-surface: var(--bg-surface);
420
+ --color-bg-elevated: var(--bg-elevated);
421
+ --color-bg-sunken: var(--bg-sunken);
422
+ --color-bg-brand: var(--bg-brand);
423
+ --color-bg-brand-subtle: var(--bg-brand-subtle);
424
+ --color-bg-brand-hover: var(--bg-brand-hover);
425
+ --color-bg-brand-bright: var(--bg-brand-bright);
426
+ --color-bg-success: var(--bg-success);
427
+ --color-bg-success-subtle: var(--bg-success-subtle);
428
+ --color-bg-warning: var(--bg-warning);
429
+ --color-bg-warning-subtle: var(--bg-warning-subtle);
430
+ --color-bg-danger: var(--bg-danger);
431
+ --color-bg-danger-subtle: var(--bg-danger-subtle);
432
+ --color-bg-teal-subtle: var(--bg-teal-subtle);
433
+ --color-bg-violet-subtle: var(--bg-violet-subtle);
434
+ --color-bg-rose-subtle: var(--bg-rose-subtle);
435
+ --color-text-primary: var(--text-primary);
436
+ --color-text-secondary: var(--text-secondary);
437
+ --color-text-tertiary: var(--text-tertiary);
438
+ --color-text-disabled: var(--text-disabled);
439
+ --color-text-inverse: var(--text-inverse);
440
+ --color-text-on-brand: var(--text-on-brand);
441
+ --color-text-on-success: var(--text-on-success);
442
+ --color-text-on-warning: var(--text-on-warning);
443
+ --color-text-on-danger: var(--text-on-danger);
444
+ --color-text-brand: var(--text-brand);
445
+ --color-text-success: var(--text-success);
446
+ --color-text-warning: var(--text-warning);
447
+ --color-text-danger: var(--text-danger);
448
+ --color-text-link: var(--text-link);
449
+ --color-text-link-hover: var(--text-link-hover);
450
+ --color-text-teal: var(--text-teal);
451
+ --color-icon-primary: var(--icon-primary);
452
+ --color-icon-secondary: var(--icon-secondary);
453
+ --color-icon-disabled: var(--icon-disabled);
454
+ --color-icon-brand: var(--icon-brand);
455
+ --color-icon-success: var(--icon-success);
456
+ --color-icon-warning: var(--icon-warning);
457
+ --color-icon-danger: var(--icon-danger);
458
+ --color-border-subtle: var(--border-subtle);
459
+ --color-border-default: var(--border-default);
460
+ --color-border-strong: var(--border-strong);
461
+ --color-border-brand: var(--border-brand);
462
+ --color-border-focus: var(--border-focus);
463
+ --color-border-success: var(--border-success);
464
+ --color-border-warning: var(--border-warning);
465
+ --color-border-danger: var(--border-danger);
466
+ }
467
+
468
+ .light {
469
+ --color-bg-primary: var(--bg-primary);
470
+ --color-bg-secondary: var(--bg-secondary);
471
+ --color-bg-surface: var(--bg-surface);
472
+ --color-bg-elevated: var(--bg-elevated);
473
+ --color-bg-sunken: var(--bg-sunken);
474
+ --color-bg-brand-subtle: var(--bg-brand-subtle);
475
+ --color-bg-brand-hover: var(--bg-brand-hover);
476
+ --color-bg-success-subtle: var(--bg-success-subtle);
477
+ --color-bg-warning: var(--bg-warning);
478
+ --color-bg-warning-subtle: var(--bg-warning-subtle);
479
+ --color-bg-danger-subtle: var(--bg-danger-subtle);
480
+ --color-bg-teal-subtle: var(--bg-teal-subtle);
481
+ --color-bg-violet-subtle: var(--bg-violet-subtle);
482
+ --color-bg-rose-subtle: var(--bg-rose-subtle);
483
+ --color-text-primary: var(--text-primary);
484
+ --color-text-secondary: var(--text-secondary);
485
+ --color-text-tertiary: var(--text-tertiary);
486
+ --color-text-disabled: var(--text-disabled);
487
+ --color-text-inverse: var(--text-inverse);
488
+ --color-text-brand: var(--text-brand);
489
+ --color-text-success: var(--text-success);
490
+ --color-text-warning: var(--text-warning);
491
+ --color-text-danger: var(--text-danger);
492
+ --color-text-link: var(--text-link);
493
+ --color-text-link-hover: var(--text-link-hover);
494
+ --color-text-teal: var(--text-teal);
495
+ --color-icon-primary: var(--icon-primary);
496
+ --color-icon-secondary: var(--icon-secondary);
497
+ --color-icon-disabled: var(--icon-disabled);
498
+ --color-icon-brand: var(--icon-brand);
499
+ --color-icon-success: var(--icon-success);
500
+ --color-icon-warning: var(--icon-warning);
501
+ --color-icon-danger: var(--icon-danger);
502
+ --color-border-subtle: var(--border-subtle);
503
+ --color-border-default: var(--border-default);
504
+ --color-border-strong: var(--border-strong);
505
+ --color-border-focus: var(--border-focus);
506
+ --color-border-success: var(--border-success);
507
+ }
508
+
509
+ /* === Fonts === */
510
+
511
+
512
+
513
+
514
+
515
+
516
+
517
+
518
+ /* === Theme Variables === */
519
+ /* The token palettes (colors, typography, motion, shadows, grid) live in
520
+ @lessly/tokens/theme.css — dark `:root` + `.light`, unprefixed names
521
+ (--bg-primary, --text-primary, ...). scripts/build-styles.mjs prepends that
522
+ file plus a generated layer of deprecated `--color-*` aliases to
523
+ dist/styles.css; only ui-specific pieces are declared here. */
524
+
525
+ /* Tailwind v4 scalar spacing unit. The Lessly preset overrides theme.spacing with a
526
+ fixed keyed scale, which suppresses Tailwind's own --spacing var; tw-animate-css
527
+ slide-in-from-*/slide-out-to-* offsets compute calc(N * var(--spacing)), so without
528
+ this they collapse to 0. :where() keeps zero specificity — a consumer that sets its
529
+ own --spacing always wins over this fallback. */
530
+ :where(:root) {
531
+ --spacing: 0.25rem;
532
+ }
533
+ :root {
534
+ /* === Nav / menu surfaces (0.3.0 sidebar) === */
535
+ /* The floating surface sits on the package's overlay pair: --menu-bg on
536
+ --bg-overlay, --menu-shadow on --shadow-overlay. theme.css re-resolves both
537
+ per theme, so `.light` needs no override for either. */
538
+ --hov-bg: color-mix(in srgb, var(--text-primary) 7%, transparent);
539
+ --menu-bg: var(--bg-overlay);
540
+ --menu-shadow: var(--shadow-overlay);
541
+ --menu-hov: var(--hov-bg);
542
+
543
+ /* === Auth / login-window surface (onboarding screens) ===
544
+ Dark and light are deliberately DIFFERENT treatments, not inverts: dark leans on
545
+ a border hairline + deep glow ground (no shadow), light drops the border and floats
546
+ the card on an elevation shadow. The glow layers can't be one token pair either —
547
+ dark pools --bg-brand, light washes --bg-brand-subtle — so both live per theme
548
+ rather than as a color-mix over a single token. */
549
+ --auth-glow-a: rgba(99, 148, 246, 0.1);
550
+ --auth-glow-b: rgba(6, 41, 125, 0.42);
551
+ --auth-card-border: var(--border-subtle);
552
+ --auth-card-shadow: none;
553
+ --auth-btn-shadow: none;
554
+ }
555
+
556
+ .light {
557
+ /* Light keeps opaque hover plates: a translucent text-mix would vanish on a
558
+ white menu. Pinned 0.3.0-prototype literals — the package has no hover-surface
559
+ roles yet, so these can't move onto tokens the way --menu-bg did. */
560
+ --hov-bg: #ffffff;
561
+ --menu-hov: #f2f4f7;
562
+
563
+ /* Auth screen — see :root above. */
564
+ --auth-glow-a: rgba(11, 76, 213, 0.09);
565
+ --auth-glow-b: rgba(228, 236, 254, 0.85);
566
+ --auth-card-border: transparent;
567
+ --auth-card-shadow: var(--shadow-lg);
568
+ --auth-btn-shadow: var(--shadow-md);
569
+ }
570
+
571
+ /* === Base === */
572
+
573
+
574
+ /* === Motion === */
575
+ /* Backs the <AutoHeight> primitive: eases a container's height as its content changes size.
576
+ Motion-token driven, so it inherits reduce-motion (the vars resolve to 0ms). A real class rather
577
+ than a Tailwind arbitrary utility, which the content scanner can drop for a newly-used class.
578
+ Overflow is NOT clipped here — the component clips only while the height is animating (so the
579
+ settled box doesn't slice focus rings or tooltips on full-width children). */
580
+ .motion-auto-height {
581
+ transition-property: height;
582
+ transition-duration: var(--motion-duration-normal);
583
+ transition-timing-function: var(--motion-easing-standard);
584
+ }
585
+
586
+ /* Backs ToggleGroup's `sliding` mark: one thumb that travels between equal-width slots instead of a
587
+ fill appearing on each. A real class for the same reason as the one above, and `slide` because the
588
+ thumb crosses the width of a slot — `ui`, the hover/focus tint step, would clip it to near a snap.
589
+ `standard` because it is on screen before and after and only moves in place. */
590
+ .motion-segment-thumb {
591
+ transition-property: transform;
592
+ transition-duration: var(--motion-duration-slide);
593
+ transition-timing-function: var(--motion-easing-standard);
594
+ }
595
+
596
+ /* === Animation layer (tw-animate-css, compiled) — see scripts/build-styles.mjs === */
597
+ /*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */
598
+ @layer properties;
599
+ .slide-in-from-left-2 {
600
+ --tw-enter-translate-x: calc(2*var(--spacing)*-1);
601
+ }
602
+ .slide-in-from-top-2 {
603
+ --tw-enter-translate-y: calc(2*var(--spacing)*-1);
604
+ }
605
+ .data-\[motion\=from-end\]\:slide-in-from-right-52 {
606
+ &[data-motion="from-end"] {
607
+ --tw-enter-translate-x: calc(52*var(--spacing));
608
+ }
609
+ }
610
+ .data-\[motion\=from-start\]\:slide-in-from-left-52 {
611
+ &[data-motion="from-start"] {
612
+ --tw-enter-translate-x: calc(52*var(--spacing)*-1);
613
+ }
614
+ }
615
+ .data-\[motion\=to-end\]\:slide-out-to-right-52 {
616
+ &[data-motion="to-end"] {
617
+ --tw-exit-translate-x: calc(52*var(--spacing));
618
+ }
619
+ }
620
+ .data-\[motion\=to-start\]\:slide-out-to-left-52 {
621
+ &[data-motion="to-start"] {
622
+ --tw-exit-translate-x: calc(52*var(--spacing)*-1);
623
+ }
624
+ }
625
+ .data-\[motion\^\=from-\]\:animate-in {
626
+ &[data-motion^="from-"] {
627
+ animation: enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none);
628
+ }
629
+ }
630
+ .data-\[motion\^\=from-\]\:fade-in {
631
+ &[data-motion^="from-"] {
632
+ --tw-enter-opacity: 0;
633
+ }
634
+ }
635
+ .data-\[motion\^\=to-\]\:animate-out {
636
+ &[data-motion^="to-"] {
637
+ animation: exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none);
638
+ }
639
+ }
640
+ .data-\[motion\^\=to-\]\:fade-out {
641
+ &[data-motion^="to-"] {
642
+ --tw-exit-opacity: 0;
643
+ }
644
+ }
645
+ .data-\[side\=bottom\]\:slide-in-from-top-2 {
646
+ &[data-side="bottom"] {
647
+ --tw-enter-translate-y: calc(2*var(--spacing)*-1);
648
+ }
649
+ }
650
+ .data-\[side\=left\]\:slide-in-from-right-2 {
651
+ &[data-side="left"] {
652
+ --tw-enter-translate-x: calc(2*var(--spacing));
653
+ }
654
+ }
655
+ .data-\[side\=right\]\:slide-in-from-left-2 {
656
+ &[data-side="right"] {
657
+ --tw-enter-translate-x: calc(2*var(--spacing)*-1);
658
+ }
659
+ }
660
+ .data-\[side\=top\]\:slide-in-from-bottom-2 {
661
+ &[data-side="top"] {
662
+ --tw-enter-translate-y: calc(2*var(--spacing));
663
+ }
664
+ }
665
+ .data-\[state\=closed\]\:animate-accordion-up {
666
+ &[data-state="closed"] {
667
+ animation: accordion-up var(--tw-animation-duration,var(--tw-duration,.2s))var(--tw-ease,ease-out)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none);
668
+ }
669
+ }
670
+ .data-\[state\=closed\]\:animate-collapsible-up {
671
+ &[data-state="closed"] {
672
+ animation: collapsible-up var(--tw-animation-duration,var(--tw-duration,.2s))var(--tw-ease,ease-out)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none);
673
+ }
674
+ }
675
+ .data-\[state\=closed\]\:animate-out {
676
+ &[data-state="closed"] {
677
+ animation: exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none);
678
+ }
679
+ }
680
+ .data-\[state\=closed\]\:fade-out-0 {
681
+ &[data-state="closed"] {
682
+ --tw-exit-opacity: calc(0/100);
683
+ --tw-exit-opacity: 0;
684
+ }
685
+ }
686
+ .data-\[state\=closed\]\:zoom-out-95 {
687
+ &[data-state="closed"] {
688
+ --tw-exit-scale: calc(95*1%);
689
+ --tw-exit-scale: .95;
690
+ }
691
+ }
692
+ .data-\[state\=closed\]\:slide-out-to-bottom {
693
+ &[data-state="closed"] {
694
+ --tw-exit-translate-y: 100%;
695
+ }
696
+ }
697
+ .data-\[state\=closed\]\:slide-out-to-left {
698
+ &[data-state="closed"] {
699
+ --tw-exit-translate-x: -100%;
700
+ }
701
+ }
702
+ .data-\[state\=closed\]\:slide-out-to-right {
703
+ &[data-state="closed"] {
704
+ --tw-exit-translate-x: 100%;
705
+ }
706
+ }
707
+ .data-\[state\=closed\]\:slide-out-to-top {
708
+ &[data-state="closed"] {
709
+ --tw-exit-translate-y: -100%;
710
+ }
711
+ }
712
+ .data-\[state\=hidden\]\:animate-out {
713
+ &[data-state="hidden"] {
714
+ animation: exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none);
715
+ }
716
+ }
717
+ .data-\[state\=hidden\]\:fade-out {
718
+ &[data-state="hidden"] {
719
+ --tw-exit-opacity: 0;
720
+ }
721
+ }
722
+ .data-\[state\=open\]\:animate-accordion-down {
723
+ &[data-state="open"] {
724
+ animation: accordion-down var(--tw-animation-duration,var(--tw-duration,.2s))var(--tw-ease,ease-out)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none);
725
+ }
726
+ }
727
+ .data-\[state\=open\]\:animate-collapsible-down {
728
+ &[data-state="open"] {
729
+ animation: collapsible-down var(--tw-animation-duration,var(--tw-duration,.2s))var(--tw-ease,ease-out)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none);
730
+ }
731
+ }
732
+ .data-\[state\=open\]\:animate-in {
733
+ &[data-state="open"] {
734
+ animation: enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none);
735
+ }
736
+ }
737
+ .data-\[state\=open\]\:fade-in-0 {
738
+ &[data-state="open"] {
739
+ --tw-enter-opacity: calc(0/100);
740
+ --tw-enter-opacity: 0;
741
+ }
742
+ }
743
+ .data-\[state\=open\]\:zoom-in-90 {
744
+ &[data-state="open"] {
745
+ --tw-enter-scale: calc(90*1%);
746
+ --tw-enter-scale: .9;
747
+ }
748
+ }
749
+ .data-\[state\=open\]\:zoom-in-95 {
750
+ &[data-state="open"] {
751
+ --tw-enter-scale: calc(95*1%);
752
+ --tw-enter-scale: .95;
753
+ }
754
+ }
755
+ .data-\[state\=open\]\:slide-in-from-bottom {
756
+ &[data-state="open"] {
757
+ --tw-enter-translate-y: 100%;
758
+ }
759
+ }
760
+ .data-\[state\=open\]\:slide-in-from-left {
761
+ &[data-state="open"] {
762
+ --tw-enter-translate-x: -100%;
763
+ }
764
+ }
765
+ .data-\[state\=open\]\:slide-in-from-right {
766
+ &[data-state="open"] {
767
+ --tw-enter-translate-x: 100%;
768
+ }
769
+ }
770
+ .data-\[state\=open\]\:slide-in-from-top {
771
+ &[data-state="open"] {
772
+ --tw-enter-translate-y: -100%;
773
+ }
774
+ }
775
+ .data-\[state\=visible\]\:animate-in {
776
+ &[data-state="visible"] {
777
+ animation: enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none);
778
+ }
779
+ }
780
+ .data-\[state\=visible\]\:fade-in {
781
+ &[data-state="visible"] {
782
+ --tw-enter-opacity: 0;
783
+ }
784
+ }
785
+ @property --tw-animation-delay {
786
+ syntax: "*";
787
+ inherits: false;
788
+ initial-value: 0s;
789
+ }
790
+ @property --tw-animation-direction {
791
+ syntax: "*";
792
+ inherits: false;
793
+ initial-value: normal;
794
+ }
795
+ @property --tw-animation-duration {
796
+ syntax: "*";
797
+ inherits: false;
798
+ }
799
+ @property --tw-animation-fill-mode {
800
+ syntax: "*";
801
+ inherits: false;
802
+ initial-value: none;
803
+ }
804
+ @property --tw-animation-iteration-count {
805
+ syntax: "*";
806
+ inherits: false;
807
+ initial-value: 1;
808
+ }
809
+ @property --tw-enter-blur {
810
+ syntax: "*";
811
+ inherits: false;
812
+ initial-value: 0;
813
+ }
814
+ @property --tw-enter-opacity {
815
+ syntax: "*";
816
+ inherits: false;
817
+ initial-value: 1;
818
+ }
819
+ @property --tw-enter-rotate {
820
+ syntax: "*";
821
+ inherits: false;
822
+ initial-value: 0;
823
+ }
824
+ @property --tw-enter-scale {
825
+ syntax: "*";
826
+ inherits: false;
827
+ initial-value: 1;
828
+ }
829
+ @property --tw-enter-translate-x {
830
+ syntax: "*";
831
+ inherits: false;
832
+ initial-value: 0;
833
+ }
834
+ @property --tw-enter-translate-y {
835
+ syntax: "*";
836
+ inherits: false;
837
+ initial-value: 0;
838
+ }
839
+ @property --tw-exit-blur {
840
+ syntax: "*";
841
+ inherits: false;
842
+ initial-value: 0;
843
+ }
844
+ @property --tw-exit-opacity {
845
+ syntax: "*";
846
+ inherits: false;
847
+ initial-value: 1;
848
+ }
849
+ @property --tw-exit-rotate {
850
+ syntax: "*";
851
+ inherits: false;
852
+ initial-value: 0;
853
+ }
854
+ @property --tw-exit-scale {
855
+ syntax: "*";
856
+ inherits: false;
857
+ initial-value: 1;
858
+ }
859
+ @property --tw-exit-translate-x {
860
+ syntax: "*";
861
+ inherits: false;
862
+ initial-value: 0;
863
+ }
864
+ @property --tw-exit-translate-y {
865
+ syntax: "*";
866
+ inherits: false;
867
+ initial-value: 0;
868
+ }
869
+ @keyframes enter {
870
+ from {
871
+ opacity: var(--tw-enter-opacity,1);
872
+ transform: translate3d(var(--tw-enter-translate-x,0),var(--tw-enter-translate-y,0),0)scale3d(var(--tw-enter-scale,1),var(--tw-enter-scale,1),var(--tw-enter-scale,1))rotate(var(--tw-enter-rotate,0));
873
+ filter: blur(var(--tw-enter-blur,0));
874
+ }
875
+ }
876
+ @keyframes exit {
877
+ to {
878
+ opacity: var(--tw-exit-opacity,1);
879
+ transform: translate3d(var(--tw-exit-translate-x,0),var(--tw-exit-translate-y,0),0)scale3d(var(--tw-exit-scale,1),var(--tw-exit-scale,1),var(--tw-exit-scale,1))rotate(var(--tw-exit-rotate,0));
880
+ filter: blur(var(--tw-exit-blur,0));
881
+ }
882
+ }
883
+ @keyframes accordion-down {
884
+ from {
885
+ height: 0;
886
+ }
887
+ to {
888
+ height: var(--radix-accordion-content-height,var(--bits-accordion-content-height,var(--reka-accordion-content-height,var(--kb-accordion-content-height,var(--ngp-accordion-content-height,auto)))));
889
+ }
890
+ }
891
+ @keyframes accordion-up {
892
+ from {
893
+ height: var(--radix-accordion-content-height,var(--bits-accordion-content-height,var(--reka-accordion-content-height,var(--kb-accordion-content-height,var(--ngp-accordion-content-height,auto)))));
894
+ }
895
+ to {
896
+ height: 0;
897
+ }
898
+ }
899
+ @keyframes collapsible-down {
900
+ from {
901
+ height: 0;
902
+ }
903
+ to {
904
+ height: var(--radix-collapsible-content-height,var(--bits-collapsible-content-height,var(--reka-collapsible-content-height,var(--kb-collapsible-content-height,auto))));
905
+ }
906
+ }
907
+ @keyframes collapsible-up {
908
+ from {
909
+ height: var(--radix-collapsible-content-height,var(--bits-collapsible-content-height,var(--reka-collapsible-content-height,var(--kb-collapsible-content-height,auto))));
910
+ }
911
+ to {
912
+ height: 0;
913
+ }
914
+ }
915
+ @layer properties {
916
+ @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
917
+ *, ::before, ::after, ::backdrop {
918
+ --tw-animation-delay: 0s;
919
+ --tw-animation-direction: normal;
920
+ --tw-animation-duration: initial;
921
+ --tw-animation-fill-mode: none;
922
+ --tw-animation-iteration-count: 1;
923
+ --tw-enter-blur: 0;
924
+ --tw-enter-opacity: 1;
925
+ --tw-enter-rotate: 0;
926
+ --tw-enter-scale: 1;
927
+ --tw-enter-translate-x: 0;
928
+ --tw-enter-translate-y: 0;
929
+ --tw-exit-blur: 0;
930
+ --tw-exit-opacity: 1;
931
+ --tw-exit-rotate: 0;
932
+ --tw-exit-scale: 1;
933
+ --tw-exit-translate-x: 0;
934
+ --tw-exit-translate-y: 0;
935
+ }
936
+ }
937
+ }
package/dist/styles.css CHANGED
@@ -643,6 +643,12 @@
643
643
  /* === Animation layer (tw-animate-css, compiled) — see scripts/build-styles.mjs === */
644
644
  /*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */
645
645
  @layer properties;
646
+ .slide-in-from-left-2 {
647
+ --tw-enter-translate-x: calc(2*var(--spacing)*-1);
648
+ }
649
+ .slide-in-from-top-2 {
650
+ --tw-enter-translate-y: calc(2*var(--spacing)*-1);
651
+ }
646
652
  .data-\[motion\=from-end\]\:slide-in-from-right-52 {
647
653
  &[data-motion="from-end"] {
648
654
  --tw-enter-translate-x: calc(52*var(--spacing));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lessly/ui",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "Lessly design system — shared UI primitives, tokens, and theme",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.30.3",
@@ -19,7 +19,8 @@
19
19
  "types": "./dist/tailwind-preset.d.ts",
20
20
  "default": "./dist/tailwind-preset.js"
21
21
  },
22
- "./styles.css": "./dist/styles.css"
22
+ "./styles.css": "./dist/styles.css",
23
+ "./styles-federated.css": "./dist/styles-federated.css"
23
24
  },
24
25
  "files": [
25
26
  "dist"