@fluixi-ui/tokens 0.4.0-alpha.1 → 0.4.0-alpha.11

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.
Files changed (61) hide show
  1. package/dist/css/accordion.css +81 -3
  2. package/dist/css/alert.css +52 -23
  3. package/dist/css/avatar.css +19 -0
  4. package/dist/css/badge.css +129 -5
  5. package/dist/css/base.css +3 -1
  6. package/dist/css/bottom-sheet.css +4 -4
  7. package/dist/css/breadcrumb.css +1 -1
  8. package/dist/css/bulk-editor.css +2 -17
  9. package/dist/css/button.css +76 -16
  10. package/dist/css/calendar.css +9 -7
  11. package/dist/css/card.css +5 -1
  12. package/dist/css/carousel.css +80 -0
  13. package/dist/css/checkbox-radio.css +55 -1
  14. package/dist/css/chip.css +175 -0
  15. package/dist/css/date-field.css +1 -1
  16. package/dist/css/date-picker.css +2 -2
  17. package/dist/css/descriptions.css +29 -0
  18. package/dist/css/drawer.css +4 -4
  19. package/dist/css/empty.css +40 -0
  20. package/dist/css/fab.css +48 -0
  21. package/dist/css/field-variants.css +139 -0
  22. package/dist/css/file-upload.css +40 -0
  23. package/dist/css/glow.css +127 -0
  24. package/dist/css/icon-button.css +42 -0
  25. package/dist/css/image.css +67 -0
  26. package/dist/css/input.css +15 -0
  27. package/dist/css/layout.css +10 -0
  28. package/dist/css/navigation-menu.css +8 -7
  29. package/dist/css/number-field.css +3 -3
  30. package/dist/css/page.css +138 -0
  31. package/dist/css/pagination.css +3 -3
  32. package/dist/css/parts/listbox.css +9 -4
  33. package/dist/css/pin-input.css +40 -0
  34. package/dist/css/progress.css +56 -0
  35. package/dist/css/rating.css +59 -0
  36. package/dist/css/scroll-area.css +44 -0
  37. package/dist/css/select-combobox.css +19 -4
  38. package/dist/css/sidenav.css +180 -10
  39. package/dist/css/slider.css +4 -4
  40. package/dist/css/speed-dial.css +85 -0
  41. package/dist/css/splitter.css +81 -0
  42. package/dist/css/statistic.css +34 -0
  43. package/dist/css/stepper.css +3 -3
  44. package/dist/css/surface.css +71 -0
  45. package/dist/css/switch.css +12 -0
  46. package/dist/css/table.css +11 -0
  47. package/dist/css/tabs.css +10 -1
  48. package/dist/css/tags-input.css +91 -0
  49. package/dist/css/text.css +124 -0
  50. package/dist/css/timeline.css +59 -0
  51. package/dist/css/toggle.css +6 -0
  52. package/dist/css/tree-view.css +70 -0
  53. package/dist/lib/css/index.d.ts +10 -12
  54. package/dist/lib/css/index.d.ts.map +1 -1
  55. package/dist/lib/css/index.js +21 -7
  56. package/dist/lib/reference/colors.d.ts.map +1 -1
  57. package/dist/lib/reference/colors.js +15 -1
  58. package/dist/preset.css +23 -1
  59. package/dist/tokens.css +52 -4
  60. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  61. package/package.json +2 -2
@@ -0,0 +1,71 @@
1
+ @layer flx.components {
2
+ /* ----------------------------------------------------------------------------
3
+ * Surface — an elevated container primitive. Background + radius are always set;
4
+ * elevation (shadow) and border are opt-in via data attributes, so it backs
5
+ * everything from a flat panel to a raised card or floating sheet.
6
+ * ------------------------------------------------------------------------- */
7
+ :where([data-surface]) {
8
+ background: var(--flx-ui-color-bg);
9
+ color: var(--flx-ui-color-fg);
10
+ border-radius: var(--flx-ui-radius-lg);
11
+ transition:
12
+ box-shadow var(--flx-ui-duration-fast) var(--flx-ui-ease-standard),
13
+ border-color var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
14
+ }
15
+ :where([data-surface][data-bordered]) {
16
+ border: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border);
17
+ }
18
+
19
+ :where([data-surface][data-radius='none']) {
20
+ border-radius: 0;
21
+ }
22
+ :where([data-surface][data-radius='sm']) {
23
+ border-radius: var(--flx-ui-radius-sm);
24
+ }
25
+ :where([data-surface][data-radius='md']) {
26
+ border-radius: var(--flx-ui-radius-md);
27
+ }
28
+ :where([data-surface][data-radius='lg']) {
29
+ border-radius: var(--flx-ui-radius-lg);
30
+ }
31
+ :where([data-surface][data-radius='xl']) {
32
+ border-radius: var(--flx-ui-radius-xl);
33
+ }
34
+ :where([data-surface][data-radius='full']) {
35
+ border-radius: var(--flx-ui-radius-full);
36
+ }
37
+
38
+ :where([data-surface][data-elevation='0']) {
39
+ box-shadow: none;
40
+ }
41
+ :where([data-surface][data-elevation='1']) {
42
+ box-shadow: var(--flx-ui-shadow-sm);
43
+ }
44
+ :where([data-surface][data-elevation='2']) {
45
+ box-shadow: var(--flx-ui-shadow-md);
46
+ }
47
+ :where([data-surface][data-elevation='3']) {
48
+ box-shadow: var(--flx-ui-shadow-lg);
49
+ }
50
+ :where([data-surface][data-elevation='4']) {
51
+ box-shadow: var(--flx-ui-shadow-xl);
52
+ }
53
+
54
+ /* In dark, shadows barely read on a near-black page, so elevation is tonal — the
55
+ surface lightens as it rises — with a hairline edge for definition. */
56
+ [data-theme='dark'] :where([data-surface][data-elevation='1']) {
57
+ background: var(--flx-ui-gray-2);
58
+ }
59
+ [data-theme='dark'] :where([data-surface][data-elevation='2']) {
60
+ background: var(--flx-ui-gray-3);
61
+ }
62
+ [data-theme='dark'] :where([data-surface][data-elevation='3']) {
63
+ background: var(--flx-ui-gray-4);
64
+ }
65
+ [data-theme='dark'] :where([data-surface][data-elevation='4']) {
66
+ background: var(--flx-ui-gray-5);
67
+ }
68
+ [data-theme='dark'] :where([data-surface]:not([data-elevation='0']):not([data-bordered])) {
69
+ border: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border);
70
+ }
71
+ }
@@ -30,4 +30,16 @@
30
30
  :where([role='switch'][data-state='checked'])::after {
31
31
  transform: translateX(16px);
32
32
  }
33
+ :where([role='switch']:hover:not([data-disabled])) {
34
+ background: var(--flx-ui-color-border-strong);
35
+ filter: brightness(0.97);
36
+ }
37
+ :where([role='switch']:focus-visible) {
38
+ outline: none;
39
+ box-shadow: var(--flx-ui-ctl-ring);
40
+ }
41
+ :where([role='switch'][data-disabled]) {
42
+ opacity: var(--flx-ui-opacity-disabled);
43
+ cursor: not-allowed;
44
+ }
33
45
  }
@@ -170,4 +170,15 @@
170
170
  :where([data-pinned='right']) {
171
171
  box-shadow: inset 1px 0 0 var(--flx-ui-color-border);
172
172
  }
173
+
174
+ /* Compact density — tighter padding + smaller type for dense data tables.
175
+ * Opt in with `data-density="compact"` on the table. */
176
+ [data-table][data-density='compact'] [data-table-head],
177
+ [data-table][data-density='compact'] [data-table-cell],
178
+ [data-table][data-density='compact'] [data-table-footer] [data-table-cell] {
179
+ padding: var(--flx-ui-space-1) var(--flx-ui-space-2);
180
+ }
181
+ [data-table][data-density='compact'] [data-table-cell] {
182
+ font-size: var(--flx-ui-font-size-xs);
183
+ }
173
184
  }
package/dist/css/tabs.css CHANGED
@@ -19,13 +19,22 @@
19
19
  margin-bottom: -1px;
20
20
  transition: color var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
21
21
  }
22
- :where([role='tab']:hover) {
22
+ :where([role='tab']:hover:not([data-disabled])) {
23
23
  color: var(--flx-ui-color-fg);
24
24
  }
25
+ :where([role='tab']:focus-visible) {
26
+ outline: none;
27
+ border-radius: var(--flx-ui-radius-sm);
28
+ box-shadow: var(--flx-ui-ctl-ring);
29
+ }
25
30
  :where([role='tab'][data-state='active']) {
26
31
  color: var(--flx-ui-color-primary-active);
27
32
  border-bottom-color: var(--flx-ui-color-primary);
28
33
  }
34
+ :where([role='tab'][data-disabled]) {
35
+ opacity: var(--flx-ui-opacity-disabled);
36
+ cursor: not-allowed;
37
+ }
29
38
  :where([role='tabpanel']) {
30
39
  padding: var(--flx-ui-space-4) 0;
31
40
  }
@@ -0,0 +1,91 @@
1
+ @layer flx.components {
2
+ /* Tags input (from @fluixi-ui/tags-input) — a chip field. The container owns the
3
+ frame + focus ring; the inner input is borderless and grows to fill the row. */
4
+ :where([data-tags-input]) {
5
+ box-sizing: border-box;
6
+ display: flex;
7
+ flex-wrap: wrap;
8
+ align-items: center;
9
+ gap: var(--flx-ui-space-1);
10
+ width: 100%;
11
+ min-height: var(--flx-ui-size-md);
12
+ padding: var(--flx-ui-space-1) var(--flx-ui-space-2);
13
+ color: var(--flx-ui-color-fg);
14
+ background: var(--flx-ui-color-bg);
15
+ border: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border-strong);
16
+ border-radius: var(--flx-ui-radius-md);
17
+ cursor: text;
18
+ transition:
19
+ border-color var(--flx-ui-duration-fast) var(--flx-ui-ease-standard),
20
+ box-shadow var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
21
+ }
22
+ :where([data-tags-input]:focus-within) {
23
+ border-color: var(--flx-ui-color-focus-ring);
24
+ box-shadow: var(--flx-ui-ctl-ring);
25
+ }
26
+ :where([data-tags-input][data-invalid]) {
27
+ border-color: var(--flx-ui-color-danger);
28
+ }
29
+ :where([data-tags-input][data-disabled]) {
30
+ opacity: 0.6;
31
+ cursor: not-allowed;
32
+ }
33
+
34
+ /* The editable input; borderless, inherits the field's type + colour. */
35
+ :where([data-tags-input-field]) {
36
+ flex: 1 1 6ch;
37
+ min-width: 6ch;
38
+ min-height: 0;
39
+ border: none;
40
+ outline: none;
41
+ box-shadow: none;
42
+ background: none;
43
+ font: inherit;
44
+ color: inherit;
45
+ padding: var(--flx-ui-space-1);
46
+ }
47
+ :where([data-tags-input-field]::placeholder) {
48
+ color: var(--flx-ui-color-fg-muted);
49
+ }
50
+
51
+ /* A committed tag chip. */
52
+ :where([data-tags-input-tag]) {
53
+ display: inline-flex;
54
+ align-items: center;
55
+ gap: var(--flx-ui-space-1);
56
+ padding: 0 var(--flx-ui-space-1) 0 var(--flx-ui-space-2);
57
+ height: calc(var(--flx-ui-size-md) - 2 * var(--flx-ui-space-2));
58
+ font-size: var(--flx-ui-font-size-sm);
59
+ color: var(--flx-ui-color-fg);
60
+ background: var(--flx-ui-color-bg-subtle);
61
+ border: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border);
62
+ border-radius: var(--flx-ui-radius-sm);
63
+ white-space: nowrap;
64
+ }
65
+ :where([data-tags-input-tag-remove]) {
66
+ display: inline-flex;
67
+ align-items: center;
68
+ justify-content: center;
69
+ width: 1.3em;
70
+ height: 1.3em;
71
+ padding: 0;
72
+ font: inherit;
73
+ line-height: 1;
74
+ color: var(--flx-ui-color-fg-muted);
75
+ background: none;
76
+ border: none;
77
+ border-radius: var(--flx-ui-radius-sm);
78
+ cursor: pointer;
79
+ transition:
80
+ color var(--flx-ui-duration-fast) var(--flx-ui-ease-standard),
81
+ background var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
82
+ }
83
+ :where([data-tags-input-tag-remove]:hover) {
84
+ color: var(--flx-ui-color-fg);
85
+ background: color-mix(in oklab, var(--flx-ui-color-fg) 10%, transparent);
86
+ }
87
+ :where([data-tags-input-tag-remove]:focus-visible) {
88
+ outline: none;
89
+ box-shadow: var(--flx-ui-ctl-ring);
90
+ }
91
+ }
@@ -0,0 +1,124 @@
1
+ @layer flx.components {
2
+ /*
3
+ * Type scale. Headings are fluid — they interpolate with the viewport between
4
+ * roughly 360px and 1280px — so a title doesn't have to be re-tuned per
5
+ * breakpoint. Body text is deliberately NOT fluid: shrinking running text on
6
+ * small screens is exactly where it hurts to read, and it is the one size a
7
+ * reader may have deliberately set.
8
+ */
9
+ :where([data-text]) {
10
+ --flx-ui-text-lines: 2;
11
+
12
+ margin: 0;
13
+ font-family: var(--flx-ui-font-family-sans);
14
+ color: var(--flx-ui-color-fg);
15
+ /* Long words (URLs, ids) break instead of forcing a scrollbar. */
16
+ overflow-wrap: break-word;
17
+ }
18
+
19
+ :where([data-text-variant='display']) {
20
+ font-size: clamp(1.875rem, 1.68rem + 0.87vw, 2.25rem);
21
+ font-weight: var(--flx-ui-font-weight-bold);
22
+ line-height: var(--flx-ui-line-height-none);
23
+ letter-spacing: var(--flx-ui-letter-spacing-tight);
24
+ }
25
+
26
+ :where([data-text-variant='h1']) {
27
+ font-size: clamp(1.5rem, 1.35rem + 0.65vw, 1.875rem);
28
+ font-weight: var(--flx-ui-font-weight-semibold);
29
+ line-height: var(--flx-ui-line-height-tight);
30
+ letter-spacing: var(--flx-ui-letter-spacing-tight);
31
+ }
32
+
33
+ :where([data-text-variant='h2']) {
34
+ font-size: clamp(1.25rem, 1.15rem + 0.43vw, 1.5rem);
35
+ font-weight: var(--flx-ui-font-weight-semibold);
36
+ line-height: var(--flx-ui-line-height-tight);
37
+ }
38
+
39
+ :where([data-text-variant='h3']) {
40
+ font-size: clamp(1.125rem, 1.08rem + 0.22vw, 1.25rem);
41
+ font-weight: var(--flx-ui-font-weight-semibold);
42
+ line-height: var(--flx-ui-line-height-tight);
43
+ }
44
+
45
+ :where([data-text-variant='h4']) {
46
+ font-size: var(--flx-ui-font-size-lg);
47
+ font-weight: var(--flx-ui-font-weight-medium);
48
+ line-height: var(--flx-ui-line-height-tight);
49
+ }
50
+
51
+ :where([data-text-variant='body']) {
52
+ font-size: var(--flx-ui-font-size-md);
53
+ line-height: var(--flx-ui-line-height-normal);
54
+ }
55
+
56
+ :where([data-text-variant='bodySm']) {
57
+ font-size: var(--flx-ui-font-size-sm);
58
+ line-height: var(--flx-ui-line-height-normal);
59
+ }
60
+
61
+ :where([data-text-variant='caption']) {
62
+ font-size: var(--flx-ui-font-size-xs);
63
+ line-height: var(--flx-ui-line-height-normal);
64
+ color: var(--flx-ui-color-fg-muted);
65
+ }
66
+
67
+ :where([data-text-variant='overline']) {
68
+ font-size: var(--flx-ui-font-size-xs);
69
+ font-weight: var(--flx-ui-font-weight-medium);
70
+ line-height: var(--flx-ui-line-height-normal);
71
+ letter-spacing: var(--flx-ui-letter-spacing-wide);
72
+ text-transform: uppercase;
73
+ color: var(--flx-ui-color-fg-muted);
74
+ }
75
+
76
+ :where([data-text-variant='label']) {
77
+ font-size: var(--flx-ui-font-size-sm);
78
+ font-weight: var(--flx-ui-font-weight-medium);
79
+ line-height: var(--flx-ui-line-height-normal);
80
+ }
81
+
82
+ /* Tone — `inherit` is the escape hatch for text inside a coloured surface. */
83
+ :where([data-text-tone='muted']) { color: var(--flx-ui-color-fg-muted); }
84
+ :where([data-text-tone='primary']) { color: var(--flx-ui-color-primary-text); }
85
+ :where([data-text-tone='danger']) { color: var(--flx-ui-color-danger-text); }
86
+ :where([data-text-tone='success']) { color: var(--flx-ui-color-success-text); }
87
+ :where([data-text-tone='warning']) { color: var(--flx-ui-color-warning-text); }
88
+ :where([data-text-tone='inherit']) { color: inherit; }
89
+
90
+ :where([data-text-align='start']) { text-align: start; }
91
+ :where([data-text-align='center']) { text-align: center; }
92
+ :where([data-text-align='end']) { text-align: end; }
93
+ :where([data-text-align='justify']) { text-align: justify; }
94
+
95
+ :where([data-text-weight='normal']) { font-weight: var(--flx-ui-font-weight-normal); }
96
+ :where([data-text-weight='medium']) { font-weight: var(--flx-ui-font-weight-medium); }
97
+ :where([data-text-weight='semibold']) { font-weight: var(--flx-ui-font-weight-semibold); }
98
+ :where([data-text-weight='bold']) { font-weight: var(--flx-ui-font-weight-bold); }
99
+
100
+ :where([data-text-wrap='nowrap']) { white-space: nowrap; }
101
+ /* Headings read better balanced; running text better with `pretty`. */
102
+ :where([data-text-wrap='balance']) { text-wrap: balance; }
103
+ :where([data-text-wrap='pretty']) { text-wrap: pretty; }
104
+
105
+ /* Single-line truncation. */
106
+ :where([data-text-ellipsis='line']) {
107
+ overflow: hidden;
108
+ text-overflow: ellipsis;
109
+ white-space: nowrap;
110
+ }
111
+
112
+ /*
113
+ * Multi-line truncation. `-webkit-line-clamp` is the only interoperable way to
114
+ * do this and is supported everywhere despite the prefix; the standard
115
+ * `line-clamp` is listed alongside so it takes over as engines ship it.
116
+ */
117
+ :where([data-text-ellipsis='clamp']) {
118
+ display: -webkit-box;
119
+ -webkit-box-orient: vertical;
120
+ -webkit-line-clamp: var(--flx-ui-text-lines);
121
+ line-clamp: var(--flx-ui-text-lines);
122
+ overflow: hidden;
123
+ }
124
+ }
@@ -0,0 +1,59 @@
1
+ @layer flx.components {
2
+ /* Timeline (from @fluixi-ui/timeline). A vertical feed: each item is an indicator
3
+ column (dot) beside its content; a connector line threads between the dots. */
4
+ :where([data-timeline]) {
5
+ display: flex;
6
+ flex-direction: column;
7
+ }
8
+ :where([data-timeline-item]) {
9
+ display: grid;
10
+ grid-template-columns: auto 1fr;
11
+ column-gap: var(--flx-ui-space-3);
12
+ --flx-timeline-dot: var(--flx-ui-color-border-strong);
13
+ }
14
+ :where([data-timeline-item][data-tone='primary']),
15
+ :where([data-timeline-item][data-tone='info']) { --flx-timeline-dot: var(--flx-ui-color-primary); }
16
+ :where([data-timeline-item][data-tone='success']) { --flx-timeline-dot: var(--flx-ui-color-success); }
17
+ :where([data-timeline-item][data-tone='warning']) { --flx-timeline-dot: var(--flx-ui-color-warning); }
18
+ :where([data-timeline-item][data-tone='danger']) { --flx-timeline-dot: var(--flx-ui-color-danger); }
19
+
20
+ /* Indicator column: the dot, with the connector line drawn below it. */
21
+ :where([data-timeline-indicator]) {
22
+ position: relative;
23
+ display: flex;
24
+ align-items: center;
25
+ justify-content: center;
26
+ width: 18px;
27
+ padding-top: 2px;
28
+ color: var(--flx-timeline-dot);
29
+ }
30
+ /* Default dot (when no custom `dot` content). */
31
+ :where([data-timeline-indicator]:empty)::before {
32
+ content: "";
33
+ width: 9px;
34
+ height: 9px;
35
+ border-radius: var(--flx-ui-radius-full);
36
+ background: var(--flx-timeline-dot);
37
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--flx-timeline-dot) 18%, transparent);
38
+ }
39
+ /* Connector line — hidden on the last item. */
40
+ :where([data-timeline-item]:not(:last-child) [data-timeline-indicator])::after {
41
+ content: "";
42
+ position: absolute;
43
+ top: 16px;
44
+ bottom: -6px;
45
+ left: 50%;
46
+ width: var(--flx-ui-border-width-thin);
47
+ transform: translateX(-50%);
48
+ background: var(--flx-ui-color-border);
49
+ }
50
+ :where([data-timeline-content]) {
51
+ min-width: 0;
52
+ padding-bottom: var(--flx-ui-space-4);
53
+ font-size: var(--flx-ui-font-size-sm);
54
+ color: var(--flx-ui-color-fg);
55
+ }
56
+ :where([data-timeline-item]:last-child [data-timeline-content]) {
57
+ padding-bottom: 0;
58
+ }
59
+ }
@@ -34,6 +34,12 @@
34
34
  border-color: var(--flx-ui-color-primary);
35
35
  color: var(--flx-ui-color-primary-active);
36
36
  }
37
+ :where([aria-pressed]:focus-visible) {
38
+ outline: none;
39
+ position: relative;
40
+ z-index: 2;
41
+ box-shadow: var(--flx-ui-ctl-ring);
42
+ }
37
43
  :where([aria-pressed][data-disabled]) {
38
44
  opacity: var(--flx-ui-opacity-disabled);
39
45
  pointer-events: none;
@@ -0,0 +1,70 @@
1
+ @layer flx.components {
2
+ /* Tree view (from @fluixi-ui/tree-view). Rows indent by depth via the
3
+ `--flx-tree-depth` custom property the component sets per item. */
4
+ :where([data-tree-view]),
5
+ :where([data-tree-item-group]) {
6
+ list-style: none;
7
+ margin: 0;
8
+ padding: 0;
9
+ }
10
+ :where([data-tree-item]) {
11
+ outline: none;
12
+ }
13
+ :where([data-tree-item-row]) {
14
+ display: flex;
15
+ align-items: center;
16
+ gap: var(--flx-ui-space-1);
17
+ padding: var(--flx-ui-space-1) var(--flx-ui-space-2);
18
+ padding-inline-start: calc(
19
+ var(--flx-ui-space-2) + var(--flx-tree-depth, 0) * var(--flx-ui-space-4)
20
+ );
21
+ border-radius: var(--flx-ui-radius-sm);
22
+ font-size: var(--flx-ui-font-size-sm);
23
+ color: var(--flx-ui-color-fg);
24
+ cursor: pointer;
25
+ user-select: none;
26
+ transition: background var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
27
+ }
28
+ :where([data-tree-item-row]:hover) {
29
+ background: var(--flx-ui-color-bg-subtle);
30
+ }
31
+ :where([data-tree-item][data-selected] > [data-tree-item-row]) {
32
+ background: var(--flx-ui-color-primary-subtle);
33
+ color: var(--flx-ui-color-primary);
34
+ }
35
+ :where([data-tree-item][data-disabled] > [data-tree-item-row]) {
36
+ opacity: 0.5;
37
+ cursor: not-allowed;
38
+ }
39
+ :where([data-tree-item]:focus-visible > [data-tree-item-row]) {
40
+ outline: none;
41
+ box-shadow: var(--flx-ui-ctl-ring);
42
+ }
43
+
44
+ /* Expand/collapse caret; points right, rotates down when the branch is open. */
45
+ :where([data-tree-item-indicator]) {
46
+ flex: none;
47
+ width: 0.62em;
48
+ height: 0.62em;
49
+ background: currentcolor;
50
+ opacity: 0.55;
51
+ clip-path: polygon(30% 15%, 30% 85%, 80% 50%);
52
+ transition: transform var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
53
+ }
54
+ :where([data-tree-item][data-state='expanded'] > [data-tree-item-row] [data-tree-item-indicator]) {
55
+ transform: rotate(90deg);
56
+ }
57
+ /* Keeps leaf labels aligned with branch labels. */
58
+ :where([data-tree-item-spacer]) {
59
+ flex: none;
60
+ width: 0.62em;
61
+ height: 0.62em;
62
+ }
63
+ :where([data-tree-item-label]) {
64
+ flex: 1;
65
+ min-width: 0;
66
+ overflow: hidden;
67
+ text-overflow: ellipsis;
68
+ white-space: nowrap;
69
+ }
70
+ }
@@ -1,20 +1,18 @@
1
1
  export declare const TOKEN_PREFIX = "flx-ui";
2
2
  /**
3
- * Cascade layer order (low → high): tokens (vars) → components (the default
4
- * "fluixi" look, `preset.css`) → skin (an alternate skin's overrides, e.g.
5
- * `@fluixi-ui/skin-material`). Consumer CSS stays unlayered, so it always wins.
6
- * Declared first in every stylesheet we emit so ordering is deterministic
7
- * regardless of load order; redeclarations are no-ops.
3
+ * Cascade layer order (low → high): reset (preflight — fluixi-css's reset lives
4
+ * here) → base (opinionated element defaults; also where a consumer's own reset/base
5
+ * belongs) → tokens (vars) → components (the default "fluixi" look, `preset.css`) →
6
+ * skin (an alternate skin's overrides, e.g. `@fluixi-ui/skin-material`). We reserve
7
+ * `reset`/`base` (never fill them) so a reset always sits below our components →
8
+ * fx (per-element `fx` rules from `@fluixi-ui/fx`, which must beat both the default
9
+ * look and any skin, since it's the local escape hatch).
10
+ * Consumer CSS stays unlayered, so it always wins. Declared first in every stylesheet
11
+ * we emit so ordering is deterministic regardless of load order; redeclarations are no-ops.
8
12
  */
9
- export declare const LAYER_ORDER = "@layer flx.tokens, flx.components, flx.skin;";
13
+ export declare const LAYER_ORDER = "@layer flx.reset, flx.base, flx.tokens, flx.components, flx.skin, flx.fx;";
10
14
  export declare function cssVar(name: string): string;
11
15
  export declare function cssVarRef(name: string): string;
12
- /**
13
- * Generate the full token stylesheet: static reference tokens and the light
14
- * scales plus semantic aliases on `:root`, with dark and high-contrast themes
15
- * overriding only the scales (and a few semantics). Dark also applies
16
- * automatically via `prefers-color-scheme` when no explicit theme is set.
17
- */
18
16
  export declare function generateTokensCss(): string;
19
17
  export declare const tokensCss: string;
20
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/css/index.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,YAAY,WAAW,CAAC;AAErC;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,iDAAiD,CAAC;AAE1E,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AA4FD;;;;;GAKG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAkC1C;AAED,eAAO,MAAM,SAAS,EAAE,MAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/css/index.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,YAAY,WAAW,CAAC;AAErC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,WAAW,8EAA8E,CAAC;AAEvG,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AA4GD,wBAAgB,iBAAiB,IAAI,MAAM,CAmC1C;AAED,eAAO,MAAM,SAAS,EAAE,MAA4B,CAAC"}
@@ -2,13 +2,17 @@ import { staticTokens } from '../reference/index.js';
2
2
  import { accentPresets, colorScales, semanticColors, semanticOverrides, } from '../reference/colors.js';
3
3
  export const TOKEN_PREFIX = 'flx-ui';
4
4
  /**
5
- * Cascade layer order (low → high): tokens (vars) → components (the default
6
- * "fluixi" look, `preset.css`) → skin (an alternate skin's overrides, e.g.
7
- * `@fluixi-ui/skin-material`). Consumer CSS stays unlayered, so it always wins.
8
- * Declared first in every stylesheet we emit so ordering is deterministic
9
- * regardless of load order; redeclarations are no-ops.
5
+ * Cascade layer order (low → high): reset (preflight — fluixi-css's reset lives
6
+ * here) → base (opinionated element defaults; also where a consumer's own reset/base
7
+ * belongs) → tokens (vars) → components (the default "fluixi" look, `preset.css`) →
8
+ * skin (an alternate skin's overrides, e.g. `@fluixi-ui/skin-material`). We reserve
9
+ * `reset`/`base` (never fill them) so a reset always sits below our components →
10
+ * fx (per-element `fx` rules from `@fluixi-ui/fx`, which must beat both the default
11
+ * look and any skin, since it's the local escape hatch).
12
+ * Consumer CSS stays unlayered, so it always wins. Declared first in every stylesheet
13
+ * we emit so ordering is deterministic regardless of load order; redeclarations are no-ops.
10
14
  */
11
- export const LAYER_ORDER = '@layer flx.tokens, flx.components, flx.skin;';
15
+ export const LAYER_ORDER = '@layer flx.reset, flx.base, flx.tokens, flx.components, flx.skin, flx.fx;';
12
16
  export function cssVar(name) {
13
17
  return `--${TOKEN_PREFIX}-${name}`;
14
18
  }
@@ -105,6 +109,15 @@ function accentBlocks(name, preset) {
105
109
  * overriding only the scales (and a few semantics). Dark also applies
106
110
  * automatically via `prefers-color-scheme` when no explicit theme is set.
107
111
  */
112
+ // A dark shadow on a dark surface is nearly invisible, so dark mode needs its
113
+ // own elevation: pure black at a much higher alpha with more spread, which reads
114
+ // as a halo of shade around a raised surface instead of a faint drop.
115
+ const darkShadows = {
116
+ [cssVar('shadow-sm')]: '0 1px 2px rgba(0, 0, 0, 0.4)',
117
+ [cssVar('shadow-md')]: '0 2px 6px rgba(0, 0, 0, 0.44), 0 1px 2px rgba(0, 0, 0, 0.3)',
118
+ [cssVar('shadow-lg')]: '0 8px 20px rgba(0, 0, 0, 0.5), 0 2px 6px rgba(0, 0, 0, 0.36)',
119
+ [cssVar('shadow-xl')]: '0 16px 36px rgba(0, 0, 0, 0.6), 0 4px 12px rgba(0, 0, 0, 0.42)',
120
+ };
108
121
  export function generateTokensCss() {
109
122
  const root = block(":root, [data-theme='light']", {
110
123
  ...staticVars(),
@@ -118,13 +131,14 @@ export function generateTokensCss() {
118
131
  ...scaleVars('dark'),
119
132
  ...semanticVars(),
120
133
  ...overrideVars('dark'),
134
+ ...darkShadows,
121
135
  });
122
136
  const highContrast = block("[data-theme='high-contrast']", {
123
137
  ...scaleVars('high-contrast'),
124
138
  ...semanticVars(),
125
139
  ...overrideVars('high-contrast'),
126
140
  });
127
- const autoDark = `@media (prefers-color-scheme: dark) {\n${block(':root:not([data-theme])', { ...scaleVars('dark'), ...semanticVars(), ...overrideVars('dark') })
141
+ const autoDark = `@media (prefers-color-scheme: dark) {\n${block(':root:not([data-theme])', { ...scaleVars('dark'), ...semanticVars(), ...overrideVars('dark'), ...darkShadows })
128
142
  .split('\n')
129
143
  .map((line) => ` ${line}`)
130
144
  .join('\n')}\n}`;
@@ -1 +1 @@
1
- {"version":3,"file":"colors.d.ts","sourceRoot":"","sources":["../../../src/lib/reference/colors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,eAAe,CAAC;AAE3D,4FAA4F;AAC5F,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAC9D,eAAO,MAAM,cAAc,EAAE,UAAmB,CAAC;AAEjD,KAAK,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAsGpC,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQjE,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAwBjD,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAUhF,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,KAAK,CAAC;IACZ,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,YAAY,CAc3E,CAAC"}
1
+ {"version":3,"file":"colors.d.ts","sourceRoot":"","sources":["../../../src/lib/reference/colors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,eAAe,CAAC;AAE3D,4FAA4F;AAC5F,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAC9D,eAAO,MAAM,cAAc,EAAE,UAAmB,CAAC;AAEjD,KAAK,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AA+GpC,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQjE,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CA6BjD,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAUhF,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,KAAK,CAAC;IACZ,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,YAAY,CAc3E,CAAC"}
@@ -66,23 +66,32 @@ const statusLight = {
66
66
  'danger-solid': '#e5484d',
67
67
  'danger-subtle': '#feebec',
68
68
  'danger-fg': '#ffffff',
69
+ // `-text`: a darker, saturated shade that stays legible ON the `-subtle`
70
+ // background (the solid shade is too light for text — notably amber).
71
+ 'danger-text': '#cd2b31',
69
72
  'success-solid': '#30a46c',
70
73
  'success-subtle': '#e6f6ec',
71
74
  'success-fg': '#ffffff',
75
+ 'success-text': '#18794e',
72
76
  'warning-solid': '#ffb224',
73
77
  'warning-subtle': '#fff4d5',
74
78
  'warning-fg': '#1c2024',
79
+ 'warning-text': '#ad5700',
75
80
  };
76
81
  const statusDark = {
77
82
  'danger-solid': '#ff6369',
78
83
  'danger-subtle': '#3b1219',
79
84
  'danger-fg': '#ffffff',
85
+ // On dark, `-text` is a bright shade legible on the dark `-subtle` bg.
86
+ 'danger-text': '#ff9592',
80
87
  'success-solid': '#3dd68c',
81
88
  'success-subtle': '#0f291e',
82
89
  'success-fg': '#ffffff',
90
+ 'success-text': '#4cc38a',
83
91
  'warning-solid': '#ffca16',
84
92
  'warning-subtle': '#3a2e00',
85
93
  'warning-fg': '#1c2024',
94
+ 'warning-text': '#ffca16',
86
95
  };
87
96
  function scaleRecord(gray, accent, status) {
88
97
  const out = {};
@@ -104,22 +113,27 @@ export const semanticColors = {
104
113
  'color-fg': 'var(--flx-ui-gray-12)',
105
114
  'color-fg-muted': 'var(--flx-ui-gray-11)',
106
115
  'color-border': 'var(--flx-ui-gray-6)',
107
- 'color-border-strong': 'var(--flx-ui-gray-8)',
116
+ 'color-border-strong': 'var(--flx-ui-gray-7)',
108
117
  'color-focus-ring': 'var(--flx-ui-accent-8)',
109
118
  'color-primary': 'var(--flx-ui-accent-9)',
110
119
  'color-primary-hover': 'var(--flx-ui-accent-10)',
111
120
  'color-primary-active': 'var(--flx-ui-accent-11)',
112
121
  'color-primary-fg': '#ffffff',
113
122
  'color-primary-subtle': 'var(--flx-ui-accent-3)',
123
+ // Legible text shade for a tinted (subtle) background.
124
+ 'color-primary-text': 'var(--flx-ui-accent-11)',
114
125
  'color-danger': 'var(--flx-ui-danger-solid)',
115
126
  'color-danger-fg': 'var(--flx-ui-danger-fg)',
116
127
  'color-danger-subtle': 'var(--flx-ui-danger-subtle)',
128
+ 'color-danger-text': 'var(--flx-ui-danger-text)',
117
129
  'color-success': 'var(--flx-ui-success-solid)',
118
130
  'color-success-fg': 'var(--flx-ui-success-fg)',
119
131
  'color-success-subtle': 'var(--flx-ui-success-subtle)',
132
+ 'color-success-text': 'var(--flx-ui-success-text)',
120
133
  'color-warning': 'var(--flx-ui-warning-solid)',
121
134
  'color-warning-fg': 'var(--flx-ui-warning-fg)',
122
135
  'color-warning-subtle': 'var(--flx-ui-warning-subtle)',
136
+ 'color-warning-text': 'var(--flx-ui-warning-text)',
123
137
  'color-overlay': 'rgba(0, 0, 0, 0.4)',
124
138
  };
125
139
  export const semanticOverrides = {