@estiva-app/ui 0.37.2 → 0.38.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.
@@ -55,14 +55,14 @@ function resolveVar(name: string): string {
55
55
 
56
56
  /* ── The vocabulary (mirrors tailwind-preset.js; tokens.test.ts holds the two together) ── */
57
57
 
58
- type Swatch = 'fill' | 'text' | 'text-inverse' | 'border' | 'outline' | 'shadow' | 'drop-shadow'
58
+ type Swatch = 'fill' | 'text' | 'text-inverse' | 'text-tooltip' | 'border' | 'outline' | 'shadow' | 'drop-shadow'
59
59
  type Token = { key: string; cssVar: string; cls: string; swatch: Swatch; note?: string }
60
60
  type Family = { label: string; blurb: string; utilities: string; tokens: Token[] }
61
61
 
62
62
  const colour = (prefix: string, key: string, swatch: Swatch, note?: string): Token => ({
63
63
  key: `${prefix}-${key}`,
64
64
  cssVar: `--${prefix}-${key}`,
65
- cls: `${swatch === 'text' || swatch === 'text-inverse' ? 'text' : swatch === 'border' || swatch === 'outline' ? 'border' : 'bg'}-${prefix}-${key}`,
65
+ cls: `${swatch === 'text' || swatch === 'text-inverse' || swatch === 'text-tooltip' ? 'text' : swatch === 'border' || swatch === 'outline' ? 'border' : 'bg'}-${prefix}-${key}`,
66
66
  swatch,
67
67
  note,
68
68
  })
@@ -86,6 +86,18 @@ const FAMILIES: Family[] = [
86
86
  utilities: COLOUR_UTILITIES,
87
87
  tokens: ['subtle', 'default', 'strong', 'focus'].map((k) => colour('border', k, 'border')),
88
88
  },
89
+ {
90
+ label: 'Tooltip',
91
+ blurb: "A tooltip's own colours, so a theme can draw it apart from its menus and popups: its ground, its text and its edge, then the same three for a shortcut key shown on it.",
92
+ utilities: COLOUR_UTILITIES,
93
+ tokens: [colour('bg', 'tooltip', 'fill'), colour('text', 'tooltip', 'text-tooltip'), colour('border', 'tooltip', 'border'), colour('bg', 'tooltip-key', 'fill'), colour('text', 'tooltip-key', 'text-tooltip'), colour('border', 'tooltip-key', 'border')],
94
+ },
95
+ {
96
+ label: 'Field',
97
+ blurb: "A field's own box, so a theme can draw its text boxes apart from the ground it uses for chips and keys: its fill, its line and its shadow.",
98
+ utilities: COLOUR_UTILITIES,
99
+ tokens: [colour('bg', 'field', 'fill'), colour('border', 'field', 'border')],
100
+ },
89
101
  {
90
102
  label: 'Accent',
91
103
  blurb: 'The brand colour: the primary button, the checked box, the brand chip. Muted is its opaque wash, wash the translucent one a row tints with on hover, outline its thin border.',
@@ -121,6 +133,7 @@ const FAMILIES: Family[] = [
121
133
  { key: 'md', cssVar: '--shadow-md', cls: 'shadow-md', swatch: 'shadow' },
122
134
  { key: 'lg', cssVar: '--shadow-lg', cls: 'shadow-lg', swatch: 'shadow' },
123
135
  { key: 'focus-ring', cssVar: '--focus-ring', cls: 'shadow-focus-ring', swatch: 'shadow' },
136
+ { key: 'field', cssVar: '--shadow-field', cls: 'shadow-field', swatch: 'shadow' },
124
137
  { key: 'glow-warning', cssVar: '--glow-warning', cls: 'shadow-glow-warning', swatch: 'shadow' },
125
138
  { key: 'highlight-inset', cssVar: '--highlight-inset', cls: 'shadow-highlight-inset', swatch: 'shadow' },
126
139
  { key: 'glow-success', cssVar: '--glow-success', cls: 'drop-shadow-glow-success', swatch: 'drop-shadow' },
@@ -180,8 +193,9 @@ function SwatchBox({ token }: { token: Token }) {
180
193
  return <div className={`${base} border border-border-subtle`} style={{ background: v }} />
181
194
  case 'text':
182
195
  case 'text-inverse':
196
+ case 'text-tooltip':
183
197
  return (
184
- <div className={`${base} flex items-center justify-center border border-border-subtle ${token.swatch === 'text-inverse' ? 'bg-accent-primary' : 'bg-bg-surface'}`}>
198
+ <div className={`${base} flex items-center justify-center border border-border-subtle ${token.swatch === 'text-inverse' ? 'bg-accent-primary' : token.swatch === 'text-tooltip' ? 'bg-bg-tooltip' : 'bg-bg-surface'}`}>
185
199
  <span className="text-body-2-strong leading-none" style={{ color: v }}>Ag</span>
186
200
  </div>
187
201
  )
@@ -70,6 +70,7 @@ export default {
70
70
  plugin(({ addVariant, addComponents }) => {
71
71
  addVariant('signal', '.signal &')
72
72
  addVariant('ship', "[data-theme='ship'] &")
73
+ addVariant('leaf', "[data-theme='leaf'] &")
73
74
  // The Signal canvas: the control-room dot grid on the app's dark ground,
74
75
  // in the Signal theme only. AppShell's floating frame carries
75
76
  // `signal-canvas`. Peek's, verbatim (UIG-14, Katerina, 19 September: the
@@ -149,6 +150,9 @@ export default {
149
150
  'bg-selected': 'var(--bg-selected)',
150
151
  'bg-active': 'var(--bg-active)',
151
152
  'bg-disabled': 'var(--bg-disabled)',
153
+ 'bg-tooltip': 'var(--bg-tooltip)',
154
+ 'bg-tooltip-key': 'var(--bg-tooltip-key)',
155
+ 'bg-field': 'var(--bg-field)',
152
156
  // text
153
157
  'text-primary': 'var(--text-primary)',
154
158
  'text-secondary': 'var(--text-secondary)',
@@ -156,11 +160,16 @@ export default {
156
160
  'text-disabled': 'var(--text-disabled)',
157
161
  'text-inverse': 'var(--text-inverse)',
158
162
  'text-interactive': 'var(--text-interactive)',
163
+ 'text-tooltip': 'var(--text-tooltip)',
164
+ 'text-tooltip-key': 'var(--text-tooltip-key)',
159
165
  // border
160
166
  'border-subtle': 'var(--border-subtle)',
161
167
  'border-default': 'var(--border-default)',
162
168
  'border-strong': 'var(--border-strong)',
163
169
  'border-focus': 'var(--border-focus)',
170
+ 'border-tooltip': 'var(--border-tooltip)',
171
+ 'border-tooltip-key': 'var(--border-tooltip-key)',
172
+ 'border-field': 'var(--border-field)',
164
173
  // accent
165
174
  'accent-primary': 'var(--accent-primary)',
166
175
  'accent-hover': 'var(--accent-hover)',
@@ -199,6 +208,7 @@ export default {
199
208
  // The ring a focused control wears where a theme wants one: Signal's
200
209
  // glow. Every theme defines it; the shared inputs use it under `signal:`.
201
210
  'focus-ring': 'var(--focus-ring)',
211
+ 'field': 'var(--shadow-field)',
202
212
  // Signal's glow and inner highlight (D16); the components use them under `signal:`.
203
213
  // All three glows are here as box shadows, and the two below are also
204
214
  // drop shadows: a glow on a surface is a box shadow, a glow on an icon
package/tokens.css CHANGED
@@ -80,6 +80,20 @@
80
80
  --highlight-inset: inset 0 1px 0 rgba(255, 255, 255, 0.035);
81
81
  --scrim: rgba(0, 0, 0, 0.5);
82
82
  --scrim-strong: rgba(0, 0, 0, 0.8);
83
+ /* Tooltips have their own colours (Katerina, 27 September); here, the ones they
84
+ always had: the elevated surface, primary text, the default border. */
85
+ --bg-tooltip: #FAFAFA;
86
+ --text-tooltip: #18181B;
87
+ --border-tooltip: #E4E4E7;
88
+ /* A shortcut key on a tooltip: what Kbd always drew here. */
89
+ --bg-tooltip-key: #F4F4F5;
90
+ --border-tooltip-key: #D4D4D8;
91
+ --text-tooltip-key: #52525B;
92
+ /* A field has its own colours (Katerina, 28 September); here, the ones it always had:
93
+ the inset ground, the default line, no shadow. */
94
+ --bg-field: #F4F4F5;
95
+ --border-field: #E4E4E7;
96
+ --shadow-field: 0 0 #0000;
83
97
  }
84
98
 
85
99
  /* ─── dark ─── Peek's dark theme (peek/src/index.css .dark, 2026-08-28). */
@@ -144,6 +158,20 @@
144
158
  --highlight-inset: inset 0 1px 0 rgba(255, 255, 255, 0.035);
145
159
  --scrim: rgba(0, 0, 0, 0.5);
146
160
  --scrim-strong: rgba(0, 0, 0, 0.8);
161
+ /* Tooltips have their own colours (Katerina, 27 September); here, the ones they
162
+ always had: the elevated surface, primary text, the default border. */
163
+ --bg-tooltip: #1C1C1F;
164
+ --text-tooltip: #FAFAFA;
165
+ --border-tooltip: #2D2D31;
166
+ /* A shortcut key on a tooltip: what Kbd always drew here. */
167
+ --bg-tooltip-key: #27272A;
168
+ --border-tooltip-key: #52525B;
169
+ --text-tooltip-key: #AEAEB6;
170
+ /* A field has its own colours (Katerina, 28 September); here, the ones it always had:
171
+ the inset ground, the default line, no shadow. */
172
+ --bg-field: #27272A;
173
+ --border-field: #2D2D31;
174
+ --shadow-field: 0 0 #0000;
147
175
  }
148
176
 
149
177
 
@@ -212,6 +240,20 @@
212
240
  --highlight-inset: inset 0 1px 0 rgba(255, 255, 255, 0.035);
213
241
  --scrim: rgba(0, 0, 0, 0.5);
214
242
  --scrim-strong: rgba(0, 0, 0, 0.8);
243
+ /* Tooltips have their own colours (Katerina, 27 September); here, the ones they
244
+ always had: the elevated surface, primary text, the default border. */
245
+ --bg-tooltip: #191d24;
246
+ --text-tooltip: #eef1f6;
247
+ --border-tooltip: rgba(255, 255, 255, 0.12);
248
+ /* A shortcut key on a tooltip: what Kbd always drew here. */
249
+ --bg-tooltip-key: rgba(255, 255, 255, 0.05);
250
+ --border-tooltip-key: rgba(255, 255, 255, 0.22);
251
+ --text-tooltip-key: #b2b9c6;
252
+ /* A field has its own colours (Katerina, 28 September); here, the ones it always had:
253
+ the inset ground, the default line, no shadow. */
254
+ --bg-field: #20242d;
255
+ --border-field: rgba(255, 255, 255, 0.12);
256
+ --shadow-field: 0 0 #0000;
215
257
  }
216
258
 
217
259
  /* ─── ship ─── Ship's palette (Katerina's ruling, 2026-08-28; ship/web/src/index.css). */
@@ -286,4 +328,106 @@
286
328
  --highlight-inset: inset 0 1px 0 rgba(255, 255, 255, 0.035);
287
329
  --scrim: rgba(0, 0, 0, 0.5);
288
330
  --scrim-strong: rgba(0, 0, 0, 0.8);
331
+ /* Tooltips have their own colours (Katerina, 27 September); here, the ones they
332
+ always had: the elevated surface, primary text, the default border. */
333
+ --bg-tooltip: #1c1f28;
334
+ --text-tooltip: #e6e8ee;
335
+ --border-tooltip: rgba(255, 255, 255, 0.12);
336
+ /* A shortcut key on a tooltip: what Kbd always drew here. */
337
+ --bg-tooltip-key: rgba(255, 255, 255, 0.05);
338
+ --border-tooltip-key: rgba(255, 255, 255, 0.22);
339
+ --text-tooltip-key: #b3b9c7;
340
+ /* A field has its own colours (Katerina, 28 September); here, the ones it always had:
341
+ the inset ground, the default line, no shadow. */
342
+ --bg-field: #1f2330;
343
+ --border-field: rgba(255, 255, 255, 0.12);
344
+ --shadow-field: 0 0 #0000;
345
+ }
346
+
347
+ /* ─── leaf ─── Leaf's theme: Linear's light theme on these same tokens (Katerina,
348
+ 27–28 September: "I like Linear's theme more", and "get it right, not by
349
+ screenshot"). Read from the Linear app itself: its named colours
350
+ (--color-bg-*, --color-border-*, --color-text-*, --focus-ring-color), dumped
351
+ from a signed-in tab on 28 September and turned from LCH into sRGB by Chrome.
352
+ Where Linear names no colour for one of these tokens, the light theme's stays
353
+ (marked "light"); menus, the toolbar and tooltips follow her rulings. */
354
+ :root[data-theme='leaf'] {
355
+ color-scheme: light;
356
+
357
+ /* bg */
358
+ --bg-base: #efeff0; /* --color-bg-quaternary, the app's ground and sidebar */
359
+ --bg-surface: #f9f9fa; /* --color-bg-primary, the content panel and header */
360
+ --bg-elevated: #ffffff; /* menus and the toolbar are white (Katerina, 27 September) */
361
+ --bg-inset: #e9e9ea; /* --color-bg-secondary: not the ground, or an inset card and a skeleton vanish on it (Katerina, 28 September) */
362
+ /* See-through, so a hover shows on a row that is itself hovered or grey (Katerina, 28 September):
363
+ 6.4% black is Linear's --color-bg-secondary (#e9e9ea) on its content panel. */
364
+ --bg-hover: rgba(0, 0, 0, 0.064);
365
+ --bg-selected: #e5e5e6; /* --color-bg-tertiary */
366
+ --bg-active: #e1e1e2; /* the selected sidebar row (lch 89.54%) */
367
+ --bg-disabled: #efeff0; /* the base, as light has it */
368
+
369
+ /* text */
370
+ --text-primary: #1b1b1b; /* --color-text-primary */
371
+ --text-secondary: #5c5c5e; /* --color-text-tertiary: labels, descriptions, icons */
372
+ --text-muted: #9c9d9f; /* --color-text-quaternary: placeholders, counts */
373
+ --text-disabled: #D4D4D8; /* light */
374
+ --text-inverse: #ffffff;
375
+ --text-interactive: #1b1b1b; /* --app-link-color: a link is the text colour */
376
+
377
+ /* borders */
378
+ /* One step darker than Linear's own faintest line (Katerina, 28 September): our cards, chips and
379
+ toasts draw their edge with subtle, and Linear's #f1f1f1 vanished on white and on the ground. */
380
+ --border-subtle: #e5e5e6; /* --color-bg-tertiary */
381
+ --border-default: #dedede; /* --color-border-secondary */
382
+ --border-strong: #d3d3d3; /* --color-border-tertiary */
383
+ --border-focus: #5e69d1; /* --focus-ring-color */
384
+
385
+ /* accent — Linear's indigo */
386
+ --accent-primary: #5e69d1; /* --focus-ring-color, the selection's colour */
387
+ --accent-hover: #6b75df; /* not named in Linear's set: the indigo beside it in the app's own values */
388
+ --accent-muted: #EDE9FE; /* light */
389
+
390
+ /* semantic — light, until Linear's are read */
391
+ --info-default: #3B82F6;
392
+ --info-muted: #DBEAFE;
393
+ --warning-default: #967000; /* Linear's darkest yellow: readable on the warning wash (Katerina, 28 September) */
394
+ --warning-muted: #FEF3C7;
395
+ --success-default: #0EA06F;
396
+ --success-muted: #D1FAE5;
397
+ --error-default: #EF4444;
398
+ --error-muted: #FEE2E2;
399
+
400
+ /* shadows — light */
401
+ --shadow-sm: 0 1px 2px 0 rgba(0,0,0,0.05);
402
+ --shadow-md: 0 4px 6px -1px rgba(0,0,0,0.10), 0 2px 4px -2px rgba(0,0,0,0.10);
403
+ --shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.10), 0 4px 6px -4px rgba(0,0,0,0.10);
404
+ --focus-ring: 0 0 0 1px #5e69d1; /* --focus-ring-outline: 1px of the accent */
405
+ /* Transparent colours (D16): light's strengths, on Linear's indigo. */
406
+ --bg-wash: rgba(0, 0, 0, 0.04);
407
+ --accent-outline: rgba(94, 105, 209, 0.3);
408
+ --info-outline: rgba(59, 130, 246, 0.3);
409
+ --warning-outline: rgba(245, 158, 11, 0.3);
410
+ --success-outline: rgba(14, 160, 111, 0.3);
411
+ --error-outline: rgba(239, 68, 68, 0.3);
412
+ --accent-wash: rgba(94, 105, 209, 0.11);
413
+ --success-wash: rgba(14, 160, 111, 0.11);
414
+ --glow-warning: 0 0 5px rgba(245, 158, 11, 0.4);
415
+ --glow-success: 0 0 5px rgba(14, 160, 111, 0.7);
416
+ --glow-accent: 0 0 5px rgba(94, 105, 209, 0.6);
417
+ --highlight-inset: inset 0 1px 0 rgba(255, 255, 255, 0.035);
418
+ --scrim: rgba(0, 0, 0, 0.5);
419
+ --scrim-strong: rgba(0, 0, 0, 0.8);
420
+ /* Tooltips are dark on the light page (Katerina, 27 September): Linear's text colour as the ground. */
421
+ --bg-tooltip: #1b1b1b;
422
+ --text-tooltip: #ffffff;
423
+ --border-tooltip: #1b1b1b;
424
+ /* A shortcut key on the dark tooltip: a faint light key. */
425
+ --bg-tooltip-key: rgba(255, 255, 255, 0.1);
426
+ --border-tooltip-key: rgba(255, 255, 255, 0.18);
427
+ --text-tooltip-key: rgba(255, 255, 255, 0.75);
428
+ /* A field is Linear's: white, a faint line, a soft two-layer shadow (read from its
429
+ comment box, 28 September: lch(100), lch(97.3), 0 3px 6px -2px at 2% and 0 1px 1px at 4%). */
430
+ --bg-field: #ffffff;
431
+ --border-field: #dedede; /* the default line, so a field shows on a white dialog (Katerina, 28 September); Linear's own is #f7f7f7 */
432
+ --shadow-field: 0 3px 6px -2px rgba(0, 0, 0, 0.02), 0 1px 1px 0 rgba(0, 0, 0, 0.04);
289
433
  }