@fluixi-ui/tokens 0.3.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.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +106 -0
  3. package/dist/css/accordion.css +7 -0
  4. package/dist/css/alert.css +42 -0
  5. package/dist/css/avatar.css +36 -0
  6. package/dist/css/badge.css +12 -0
  7. package/dist/css/base.css +10 -0
  8. package/dist/css/bulk-editor.css +478 -0
  9. package/dist/css/button.css +92 -0
  10. package/dist/css/calendar.css +140 -0
  11. package/dist/css/checkbox-radio.css +66 -0
  12. package/dist/css/command.css +90 -0
  13. package/dist/css/date-field.css +58 -0
  14. package/dist/css/date-picker.css +81 -0
  15. package/dist/css/field.css +19 -0
  16. package/dist/css/hover-card.css +16 -0
  17. package/dist/css/input.css +85 -0
  18. package/dist/css/kbd.css +20 -0
  19. package/dist/css/menubar.css +27 -0
  20. package/dist/css/navigation-menu.css +144 -0
  21. package/dist/css/number-field.css +83 -0
  22. package/dist/css/parts/listbox.css +111 -0
  23. package/dist/css/parts/menu.css +69 -0
  24. package/dist/css/parts/motion.css +34 -0
  25. package/dist/css/parts/overlays.css +36 -0
  26. package/dist/css/progress.css +40 -0
  27. package/dist/css/select-combobox.css +76 -0
  28. package/dist/css/separator.css +15 -0
  29. package/dist/css/skeleton.css +22 -0
  30. package/dist/css/slider.css +71 -0
  31. package/dist/css/spinner.css +17 -0
  32. package/dist/css/switch.css +33 -0
  33. package/dist/css/table.css +173 -0
  34. package/dist/css/tabs.css +32 -0
  35. package/dist/css/toast.css +154 -0
  36. package/dist/css/toggle.css +74 -0
  37. package/dist/css/toolbar.css +48 -0
  38. package/dist/css/tooltip.css +13 -0
  39. package/dist/index.d.ts +6 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +5 -0
  42. package/dist/lib/css/index.d.ts +20 -0
  43. package/dist/lib/css/index.d.ts.map +1 -0
  44. package/dist/lib/css/index.js +135 -0
  45. package/dist/lib/reference/colors.d.ts +24 -0
  46. package/dist/lib/reference/colors.d.ts.map +1 -0
  47. package/dist/lib/reference/colors.js +150 -0
  48. package/dist/lib/reference/index.d.ts +302 -0
  49. package/dist/lib/reference/index.d.ts.map +1 -0
  50. package/dist/lib/reference/index.js +147 -0
  51. package/dist/lib/theme/index.d.ts +32 -0
  52. package/dist/lib/theme/index.d.ts.map +1 -0
  53. package/dist/lib/theme/index.js +87 -0
  54. package/dist/lib/vars/index.d.ts +131 -0
  55. package/dist/lib/vars/index.d.ts.map +1 -0
  56. package/dist/lib/vars/index.js +40 -0
  57. package/dist/preset.css +47 -0
  58. package/dist/tsconfig.lib.tsbuildinfo +1 -0
  59. package/package.json +79 -0
@@ -0,0 +1,66 @@
1
+ @layer flx.components {
2
+ /* ----------------------------------------------------------------------------
3
+ * Checkbox & Radio
4
+ * ------------------------------------------------------------------------- */
5
+ :where([role='checkbox']) {
6
+ box-sizing: border-box;
7
+ width: 18px;
8
+ height: 18px;
9
+ flex: none;
10
+ display: inline-flex;
11
+ align-items: center;
12
+ justify-content: center;
13
+ cursor: pointer;
14
+ border: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border-strong);
15
+ border-radius: var(--flx-ui-radius-sm);
16
+ background: var(--flx-ui-color-bg);
17
+ color: var(--flx-ui-color-primary-fg);
18
+ transition:
19
+ background var(--flx-ui-duration-fast) var(--flx-ui-ease-standard),
20
+ border-color var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
21
+ }
22
+ :where([role='checkbox'][data-state='checked'], [role='checkbox'][data-state='indeterminate']) {
23
+ background: var(--flx-ui-color-primary);
24
+ border-color: var(--flx-ui-color-primary);
25
+ }
26
+ :where([role='checkbox'][data-state='checked'])::after {
27
+ content: '';
28
+ width: 5px;
29
+ height: 9px;
30
+ margin-top: -1px;
31
+ border: solid currentColor;
32
+ border-width: 0 2px 2px 0;
33
+ transform: rotate(45deg);
34
+ }
35
+ :where([role='checkbox'][data-state='indeterminate'])::after {
36
+ content: '';
37
+ width: 9px;
38
+ height: 2px;
39
+ background: currentColor;
40
+ border-radius: 1px;
41
+ }
42
+
43
+ :where([role='radio']) {
44
+ box-sizing: border-box;
45
+ width: 18px;
46
+ height: 18px;
47
+ flex: none;
48
+ position: relative;
49
+ cursor: pointer;
50
+ border: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border-strong);
51
+ border-radius: var(--flx-ui-radius-full);
52
+ background: var(--flx-ui-color-bg);
53
+ transition: border-color var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
54
+ }
55
+ :where([role='radio'][data-state='checked']) {
56
+ border-color: var(--flx-ui-color-primary);
57
+ border-width: var(--flx-ui-border-width-thick);
58
+ }
59
+ :where([role='radio'][data-state='checked'])::after {
60
+ content: '';
61
+ position: absolute;
62
+ inset: 3px;
63
+ border-radius: var(--flx-ui-radius-full);
64
+ background: var(--flx-ui-color-primary);
65
+ }
66
+ }
@@ -0,0 +1,90 @@
1
+ @layer flx.components {
2
+ /* ----------------------------------------------------------------------------
3
+ * Command palette — a filtered action list, inline or in a dialog (⌘K).
4
+ * ------------------------------------------------------------------------- */
5
+ :where([data-command]) {
6
+ display: flex;
7
+ flex-direction: column;
8
+ overflow: hidden;
9
+ background: var(--flx-ui-color-bg);
10
+ color: var(--flx-ui-color-fg);
11
+ border: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border);
12
+ border-radius: var(--flx-ui-radius-lg);
13
+ }
14
+ :where([data-command-input]) {
15
+ flex: none;
16
+ width: 100%;
17
+ box-sizing: border-box;
18
+ padding: var(--flx-ui-space-3) var(--flx-ui-space-4);
19
+ font: inherit;
20
+ font-size: var(--flx-ui-font-size-md);
21
+ color: var(--flx-ui-color-fg);
22
+ background: transparent;
23
+ border: none;
24
+ border-bottom: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border);
25
+ outline: none;
26
+ }
27
+ :where([data-command-input]::placeholder) {
28
+ color: var(--flx-ui-color-fg-muted);
29
+ }
30
+ :where([data-command-list]) {
31
+ overflow-y: auto;
32
+ max-height: min(360px, 50vh);
33
+ padding: var(--flx-ui-space-1);
34
+ scroll-padding-block: var(--flx-ui-space-1);
35
+ }
36
+ :where([data-command-empty]) {
37
+ padding: var(--flx-ui-space-6) var(--flx-ui-space-4);
38
+ text-align: center;
39
+ font-size: var(--flx-ui-font-size-sm);
40
+ color: var(--flx-ui-color-fg-muted);
41
+ }
42
+ :where([data-command-group][data-empty]) {
43
+ display: none;
44
+ }
45
+ :where([data-command-group-heading]) {
46
+ padding: var(--flx-ui-space-2) var(--flx-ui-space-3) var(--flx-ui-space-1);
47
+ font-size: var(--flx-ui-font-size-xs);
48
+ font-weight: var(--flx-ui-font-weight-semibold);
49
+ letter-spacing: var(--flx-ui-letter-spacing-wide);
50
+ text-transform: uppercase;
51
+ color: var(--flx-ui-color-fg-muted);
52
+ }
53
+ :where([data-command-item]) {
54
+ display: flex;
55
+ align-items: center;
56
+ gap: var(--flx-ui-space-2);
57
+ padding: var(--flx-ui-space-2) var(--flx-ui-space-3);
58
+ border-radius: var(--flx-ui-radius-sm);
59
+ font-size: var(--flx-ui-font-size-sm);
60
+ color: var(--flx-ui-color-fg);
61
+ cursor: pointer;
62
+ user-select: none;
63
+ }
64
+ /* Active follows keyboard OR pointer — a single highlight, no separate :hover. */
65
+ :where([data-command-item][data-active]) {
66
+ background: var(--flx-ui-color-primary-subtle);
67
+ color: var(--flx-ui-color-primary-active);
68
+ }
69
+ :where([data-command-item][data-disabled]) {
70
+ opacity: var(--flx-ui-opacity-disabled);
71
+ pointer-events: none;
72
+ }
73
+ :where([data-command-separator]) {
74
+ height: 1px;
75
+ margin: var(--flx-ui-space-1) 0;
76
+ background: var(--flx-ui-color-border);
77
+ }
78
+ /* In a dialog the dialog surface is bare; the command is the elevated surface. */
79
+ :where([data-command-dialog]) {
80
+ padding: 0;
81
+ overflow: visible;
82
+ background: transparent;
83
+ border: none;
84
+ box-shadow: none;
85
+ width: min(560px, calc(100vw - var(--flx-ui-space-8)));
86
+ }
87
+ :where([data-command-dialog] [data-command]) {
88
+ box-shadow: var(--flx-ui-shadow-lg);
89
+ }
90
+ }
@@ -0,0 +1,58 @@
1
+ @layer flx.components {
2
+ /* ----------------------------------------------------------------------------
3
+ * DateField
4
+ * ------------------------------------------------------------------------- */
5
+ :where([data-date-field]) {
6
+ display: inline-flex;
7
+ flex-direction: column;
8
+ gap: var(--flx-ui-space-1);
9
+ }
10
+ :where([data-date-field-label]) {
11
+ font-size: var(--flx-ui-font-size-sm);
12
+ font-weight: var(--flx-ui-font-weight-medium);
13
+ color: var(--flx-ui-color-fg);
14
+ line-height: var(--flx-ui-leading-none);
15
+ }
16
+ :where([data-date-field-input]) {
17
+ display: inline-flex;
18
+ align-items: center;
19
+ padding: var(--flx-ui-space-2) var(--flx-ui-space-3);
20
+ border: 1px solid var(--flx-ui-color-border);
21
+ border-radius: var(--flx-ui-radius-md);
22
+ background: var(--flx-ui-color-bg);
23
+ font-size: var(--flx-ui-font-size-sm);
24
+ font-variant-numeric: tabular-nums;
25
+ color: var(--flx-ui-color-fg);
26
+ }
27
+ :where([data-date-field-input]:focus-within) {
28
+ outline: none;
29
+ box-shadow: var(--flx-ui-ctl-ring);
30
+ border-color: var(--flx-ui-color-primary);
31
+ }
32
+ :where([data-date-field-input][data-invalid]) {
33
+ border-color: var(--flx-ui-color-danger, #dc2626);
34
+ }
35
+ :where([data-date-field-input][aria-disabled='true']) {
36
+ color: var(--flx-ui-color-fg-subtle);
37
+ background: var(--flx-ui-color-bg-subtle);
38
+ }
39
+ :where([data-date-field-literal]) {
40
+ padding: 0 1px;
41
+ color: var(--flx-ui-color-fg-muted);
42
+ }
43
+ :where([data-date-field-segment]) {
44
+ padding: 0 2px;
45
+ border-radius: var(--flx-ui-radius-sm, 4px);
46
+ outline: none;
47
+ text-align: center;
48
+ font-variant-numeric: tabular-nums;
49
+ cursor: default;
50
+ }
51
+ :where([data-date-field-segment][data-placeholder]) {
52
+ color: var(--flx-ui-color-fg-subtle);
53
+ }
54
+ :where([data-date-field-segment]:focus) {
55
+ background: var(--flx-ui-color-primary);
56
+ color: var(--flx-ui-color-primary-fg);
57
+ }
58
+ }
@@ -0,0 +1,81 @@
1
+ @layer flx.components {
2
+ /* ----------------------------------------------------------------------------
3
+ * DatePicker
4
+ * ------------------------------------------------------------------------- */
5
+ :where([data-date-picker]) {
6
+ display: inline-flex;
7
+ flex-direction: column;
8
+ gap: var(--flx-ui-space-1);
9
+ }
10
+ :where([data-date-picker-label]) {
11
+ font-size: var(--flx-ui-font-size-sm);
12
+ font-weight: var(--flx-ui-font-weight-medium);
13
+ color: var(--flx-ui-color-fg);
14
+ line-height: var(--flx-ui-leading-none);
15
+ }
16
+ /* The control box owns the border; the embedded field input goes borderless. */
17
+ :where([data-date-picker-control]) {
18
+ display: inline-flex;
19
+ align-items: center;
20
+ gap: var(--flx-ui-space-1);
21
+ padding-right: var(--flx-ui-space-1);
22
+ border: 1px solid var(--flx-ui-color-border);
23
+ border-radius: var(--flx-ui-radius-md);
24
+ background: var(--flx-ui-color-bg);
25
+ }
26
+ :where([data-date-picker-control]:focus-within) {
27
+ box-shadow: var(--flx-ui-ctl-ring);
28
+ border-color: var(--flx-ui-color-primary);
29
+ }
30
+ :where([data-date-picker-input]) {
31
+ border: none;
32
+ box-shadow: none;
33
+ }
34
+ /* Range control: start – end. The separator sits between the two fields. */
35
+ :where([data-date-picker-control][data-date-range]) {
36
+ padding-left: var(--flx-ui-space-2);
37
+ }
38
+ :where([data-date-range-separator]) {
39
+ padding: 0 2px;
40
+ color: var(--flx-ui-color-fg-muted);
41
+ }
42
+ :where([data-date-picker-input]:focus-within) {
43
+ box-shadow: none;
44
+ border: none;
45
+ }
46
+ :where([data-date-picker-trigger]) {
47
+ display: inline-flex;
48
+ align-items: center;
49
+ justify-content: center;
50
+ width: 1.75rem;
51
+ height: 1.75rem;
52
+ padding: 0;
53
+ border: none;
54
+ border-radius: var(--flx-ui-radius-sm, 4px);
55
+ background: transparent;
56
+ color: var(--flx-ui-color-fg-muted);
57
+ cursor: pointer;
58
+ font-size: var(--flx-ui-font-size-sm);
59
+ }
60
+ :where([data-date-picker-trigger]:hover:not([disabled])) {
61
+ background: var(--flx-ui-color-bg-subtle);
62
+ color: var(--flx-ui-color-fg);
63
+ }
64
+ :where([data-date-picker-trigger]:focus-visible) {
65
+ outline: none;
66
+ box-shadow: var(--flx-ui-ctl-ring);
67
+ }
68
+ :where([data-date-picker-content]) {
69
+ padding: var(--flx-ui-space-3);
70
+ border: 1px solid var(--flx-ui-color-border);
71
+ border-radius: var(--flx-ui-radius-lg);
72
+ background: var(--flx-ui-color-bg);
73
+ box-shadow: var(--flx-ui-shadow-lg, 0 10px 30px rgba(17, 24, 39, 0.12));
74
+ animation: flx-ui-pop-in var(--flx-ui-duration-fast) var(--flx-ui-easing-standard);
75
+ }
76
+ @media (prefers-reduced-motion: reduce) {
77
+ :where([data-date-picker-content]) {
78
+ animation: none;
79
+ }
80
+ }
81
+ }
@@ -0,0 +1,19 @@
1
+ @layer flx.components {
2
+ /* ----------------------------------------------------------------------------
3
+ * Field (label + input + description + error)
4
+ * ------------------------------------------------------------------------- */
5
+ :where([data-field]) {
6
+ display: flex;
7
+ flex-direction: column;
8
+ gap: var(--flx-ui-space-1);
9
+ }
10
+ :where([data-field-label]) {
11
+ font-size: var(--flx-ui-font-size-sm);
12
+ font-weight: var(--flx-ui-font-weight-medium);
13
+ color: var(--flx-ui-color-fg);
14
+ }
15
+ :where([data-field-label][data-required])::after {
16
+ content: ' *';
17
+ color: var(--flx-ui-color-danger);
18
+ }
19
+ }
@@ -0,0 +1,16 @@
1
+ @layer flx.components {
2
+ /* ----------------------------------------------------------------------------
3
+ * Hover card
4
+ * ------------------------------------------------------------------------- */
5
+ :where([data-hovercard-content]) {
6
+ min-width: 240px;
7
+ max-width: 320px;
8
+ padding: var(--flx-ui-space-4);
9
+ background: var(--flx-ui-color-bg);
10
+ color: var(--flx-ui-color-fg);
11
+ border: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border);
12
+ border-radius: var(--flx-ui-radius-lg);
13
+ box-shadow: var(--flx-ui-shadow-lg);
14
+ animation: flx-ui-pop-in var(--flx-ui-duration-fast) var(--flx-ui-ease-emphasized);
15
+ }
16
+ }
@@ -0,0 +1,85 @@
1
+ @layer flx.components {
2
+ /* Input & textarea (from @fluixi-ui/input) — standalone or inside a field. */
3
+ :where([data-input]) {
4
+ box-sizing: border-box;
5
+ width: 100%;
6
+ min-height: var(--flx-ui-size-md);
7
+ padding: var(--flx-ui-space-2) var(--flx-ui-space-3);
8
+ font: inherit;
9
+ color: var(--flx-ui-color-fg);
10
+ background: var(--flx-ui-color-bg);
11
+ border: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border-strong);
12
+ border-radius: var(--flx-ui-radius-md);
13
+ transition:
14
+ border-color var(--flx-ui-duration-fast) var(--flx-ui-ease-standard),
15
+ box-shadow var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
16
+ }
17
+ :where(textarea[data-input]) {
18
+ min-height: calc(var(--flx-ui-size-md) * 2);
19
+ resize: vertical;
20
+ }
21
+ :where([data-input]::placeholder) {
22
+ color: var(--flx-ui-color-fg-muted);
23
+ }
24
+ :where([data-input]:focus-visible) {
25
+ outline: none;
26
+ border-color: var(--flx-ui-color-focus-ring);
27
+ box-shadow: var(--flx-ui-ctl-ring);
28
+ }
29
+ :where([data-input][data-invalid]) {
30
+ border-color: var(--flx-ui-color-danger);
31
+ }
32
+
33
+ /* Input with leading/trailing adornments: the group owns the frame. */
34
+ :where([data-input-group]) {
35
+ box-sizing: border-box;
36
+ display: inline-flex;
37
+ align-items: center;
38
+ gap: var(--flx-ui-space-2);
39
+ width: 100%;
40
+ min-height: var(--flx-ui-size-md);
41
+ padding: 0 var(--flx-ui-space-3);
42
+ color: var(--flx-ui-color-fg);
43
+ background: var(--flx-ui-color-bg);
44
+ border: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border-strong);
45
+ border-radius: var(--flx-ui-radius-md);
46
+ transition:
47
+ border-color var(--flx-ui-duration-fast) var(--flx-ui-ease-standard),
48
+ box-shadow var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
49
+ }
50
+ :where([data-input-group]:focus-within) {
51
+ border-color: var(--flx-ui-color-focus-ring);
52
+ box-shadow: var(--flx-ui-ctl-ring);
53
+ }
54
+ :where([data-input-group][data-invalid]) {
55
+ border-color: var(--flx-ui-color-danger);
56
+ }
57
+ :where([data-input-group] > input) {
58
+ flex: 1;
59
+ min-width: 0;
60
+ min-height: 0;
61
+ border: none;
62
+ outline: none;
63
+ box-shadow: none;
64
+ background: none;
65
+ font: inherit;
66
+ color: inherit;
67
+ padding: 0;
68
+ }
69
+ :where([data-input-adornment]) {
70
+ display: inline-flex;
71
+ align-items: center;
72
+ flex: none;
73
+ color: var(--flx-ui-color-fg-muted);
74
+ }
75
+ :where([data-field-description]) {
76
+ margin: 0;
77
+ font-size: var(--flx-ui-font-size-xs);
78
+ color: var(--flx-ui-color-fg-muted);
79
+ }
80
+ :where([data-field-error]) {
81
+ margin: 0;
82
+ font-size: var(--flx-ui-font-size-xs);
83
+ color: var(--flx-ui-color-danger);
84
+ }
85
+ }
@@ -0,0 +1,20 @@
1
+ @layer flx.components {
2
+ /* ----------------------------------------------------------------------------
3
+ * Kbd
4
+ * ------------------------------------------------------------------------- */
5
+ :where([data-kbd]) {
6
+ display: inline-flex;
7
+ align-items: center;
8
+ justify-content: center;
9
+ min-width: 1.6em;
10
+ padding: 1px var(--flx-ui-space-2);
11
+ font-family: var(--flx-ui-font-family-mono);
12
+ font-size: var(--flx-ui-font-size-xs);
13
+ line-height: 1.5;
14
+ color: var(--flx-ui-color-fg-muted);
15
+ background: var(--flx-ui-color-bg-subtle);
16
+ border: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border);
17
+ border-bottom-width: 2px;
18
+ border-radius: var(--flx-ui-radius-sm);
19
+ }
20
+ }
@@ -0,0 +1,27 @@
1
+ @layer flx.components {
2
+ /* Menubar: a horizontal bar of menu triggers */
3
+ :where([role='menubar']) {
4
+ display: inline-flex;
5
+ align-items: center;
6
+ gap: var(--flx-ui-space-1);
7
+ padding: var(--flx-ui-space-1);
8
+ background: var(--flx-ui-color-bg);
9
+ border: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border);
10
+ border-radius: var(--flx-ui-radius-md);
11
+ }
12
+ :where([role='menubar'] > [role='menuitem']) {
13
+ font-weight: var(--flx-ui-font-weight-medium);
14
+ }
15
+ /* Top-level triggers open a menu but aren't submenu rows — drop the ›/space-between
16
+ the shared `[aria-haspopup='menu']` rule adds. */
17
+ :where([role='menubar'] > [role='menuitem'][aria-haspopup='menu']) {
18
+ justify-content: center;
19
+ }
20
+ :where([role='menubar'] > [role='menuitem'][aria-haspopup='menu'])::after {
21
+ content: none;
22
+ }
23
+ :where([role='menubar'] > [role='menuitem'][data-state='open']) {
24
+ background: var(--flx-ui-color-primary-subtle);
25
+ color: var(--flx-ui-color-primary-active);
26
+ }
27
+ }
@@ -0,0 +1,144 @@
1
+ @layer flx.components {
2
+ /* Navigation menu: a row of links, some revealing a floating panel of links. */
3
+ :where([data-navigation-menu-list]) {
4
+ position: relative; /* anchors the indicator */
5
+ display: flex;
6
+ align-items: center;
7
+ gap: var(--flx-ui-space-1);
8
+ margin: 0;
9
+ padding: 0;
10
+ list-style: none;
11
+ }
12
+ :where([data-navigation-menu-list][data-orientation='vertical']) {
13
+ flex-direction: column;
14
+ align-items: stretch;
15
+ }
16
+ :where([data-navigation-menu-item]) {
17
+ display: flex;
18
+ }
19
+ :where([data-navigation-menu-trigger], [data-navigation-menu-list] > [data-navigation-menu-item] > [data-navigation-menu-link]) {
20
+ display: inline-flex;
21
+ align-items: center;
22
+ gap: var(--flx-ui-space-1);
23
+ padding: var(--flx-ui-space-2) var(--flx-ui-space-3);
24
+ font-size: var(--flx-ui-font-size-sm);
25
+ font-weight: var(--flx-ui-font-weight-medium);
26
+ color: var(--flx-ui-color-fg);
27
+ background: transparent;
28
+ border: none;
29
+ border-radius: var(--flx-ui-radius-md);
30
+ cursor: pointer;
31
+ outline: none;
32
+ text-decoration: none;
33
+ transition: background var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
34
+ }
35
+ :where([data-navigation-menu-trigger]:hover, [data-navigation-menu-list] > [data-navigation-menu-item] > [data-navigation-menu-link]:hover) {
36
+ background: var(--flx-ui-color-bg-subtle);
37
+ }
38
+ :where([data-navigation-menu-trigger][data-state='open']) {
39
+ background: var(--flx-ui-color-primary-subtle);
40
+ color: var(--flx-ui-color-primary-active);
41
+ }
42
+ :where([data-navigation-menu-trigger]:focus-visible, [data-navigation-menu-link]:focus-visible) {
43
+ box-shadow: var(--flx-ui-ctl-ring);
44
+ }
45
+ /* The chevron affordance: rotates when the panel opens. */
46
+ :where([data-navigation-menu-trigger])::after {
47
+ content: '';
48
+ width: 0.55em;
49
+ height: 0.55em;
50
+ margin-inline-start: var(--flx-ui-space-1);
51
+ border-right: 1.5px solid currentColor;
52
+ border-bottom: 1.5px solid currentColor;
53
+ transform: rotate(45deg) translateY(-0.1em);
54
+ opacity: 0.6;
55
+ transition: transform var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
56
+ }
57
+ :where([data-navigation-menu-trigger][data-state='open'])::after {
58
+ transform: rotate(225deg) translateY(-0.1em);
59
+ }
60
+
61
+ :where([data-navigation-menu-content]) {
62
+ display: grid;
63
+ gap: var(--flx-ui-space-1);
64
+ min-width: 220px;
65
+ padding: var(--flx-ui-space-2);
66
+ color: var(--flx-ui-color-fg);
67
+ }
68
+ /* Standalone (no viewport): the panel is its own floating surface. */
69
+ :where([data-navigation-menu-content][data-floating]) {
70
+ background: var(--flx-ui-color-bg);
71
+ border: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border);
72
+ border-radius: var(--flx-ui-radius-lg);
73
+ box-shadow: var(--flx-ui-shadow-lg);
74
+ animation: flx-ui-pop-in var(--flx-ui-duration-fast) var(--flx-ui-ease-emphasized);
75
+ }
76
+ /* Inside a viewport the surface belongs to the viewport; size to content so the
77
+ viewport can measure and animate to our natural width. */
78
+ :where([data-navigation-menu-content]:not([data-floating])) {
79
+ width: max-content;
80
+ }
81
+
82
+ /* The shared viewport: one surface every panel teleports into, morphing size. */
83
+ :where([data-navigation-menu-viewport][data-state='closed']) {
84
+ display: none;
85
+ }
86
+ :where([data-navigation-menu-viewport]) {
87
+ overflow: hidden;
88
+ width: var(--flx-ui-navigation-menu-viewport-width, auto);
89
+ height: var(--flx-ui-navigation-menu-viewport-height, auto);
90
+ background: var(--flx-ui-color-bg);
91
+ color: var(--flx-ui-color-fg);
92
+ border: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border);
93
+ border-radius: var(--flx-ui-radius-lg);
94
+ box-shadow: var(--flx-ui-shadow-lg);
95
+ transition:
96
+ width var(--flx-ui-duration-normal) var(--flx-ui-ease-emphasized),
97
+ height var(--flx-ui-duration-normal) var(--flx-ui-ease-emphasized),
98
+ left var(--flx-ui-duration-normal) var(--flx-ui-ease-emphasized),
99
+ top var(--flx-ui-duration-normal) var(--flx-ui-ease-emphasized);
100
+ animation: flx-ui-pop-in var(--flx-ui-duration-fast) var(--flx-ui-ease-emphasized);
101
+ }
102
+
103
+ /* The indicator: a small arrow that slides to sit under the active trigger. */
104
+ :where([data-navigation-menu-indicator]) {
105
+ position: absolute;
106
+ top: 100%;
107
+ left: 0;
108
+ display: flex;
109
+ justify-content: center;
110
+ width: var(--flx-ui-navigation-menu-indicator-size, 0);
111
+ height: var(--flx-ui-space-2);
112
+ transform: translateX(var(--flx-ui-navigation-menu-indicator-x, 0));
113
+ pointer-events: none;
114
+ transition:
115
+ transform var(--flx-ui-duration-normal) var(--flx-ui-ease-emphasized),
116
+ width var(--flx-ui-duration-normal) var(--flx-ui-ease-emphasized);
117
+ }
118
+ :where([data-navigation-menu-indicator])::before {
119
+ content: '';
120
+ width: var(--flx-ui-space-2);
121
+ height: var(--flx-ui-space-2);
122
+ background: var(--flx-ui-color-bg);
123
+ border-top: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border);
124
+ border-left: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border);
125
+ transform: rotate(45deg) translateY(25%);
126
+ border-top-left-radius: 2px;
127
+ }
128
+
129
+ :where([data-navigation-menu-content] [data-navigation-menu-link]) {
130
+ display: block;
131
+ padding: var(--flx-ui-space-2) var(--flx-ui-space-3);
132
+ border-radius: var(--flx-ui-radius-sm);
133
+ color: var(--flx-ui-color-fg);
134
+ text-decoration: none;
135
+ outline: none;
136
+ }
137
+ :where([data-navigation-menu-content] [data-navigation-menu-link]:hover, [data-navigation-menu-content] [data-navigation-menu-link]:focus-visible) {
138
+ background: var(--flx-ui-color-primary-subtle);
139
+ color: var(--flx-ui-color-primary-active);
140
+ }
141
+ :where([data-navigation-menu-link][data-active]) {
142
+ color: var(--flx-ui-color-primary-active);
143
+ }
144
+ }
@@ -0,0 +1,83 @@
1
+ @layer flx.components {
2
+ /* ----------------------------------------------------------------------------
3
+ * NumberField
4
+ * ------------------------------------------------------------------------- */
5
+ :where([data-number-field]) {
6
+ display: inline-flex;
7
+ flex-direction: column;
8
+ gap: var(--flx-ui-space-1);
9
+ }
10
+ :where([data-number-field-label]) {
11
+ font-size: var(--flx-ui-font-size-sm);
12
+ font-weight: var(--flx-ui-font-weight-medium);
13
+ color: var(--flx-ui-color-fg);
14
+ line-height: var(--flx-ui-leading-none);
15
+ }
16
+ :where([data-number-field-label][data-disabled]) {
17
+ color: var(--flx-ui-color-fg-subtle);
18
+ }
19
+ /* Control row: decrement | input | increment */
20
+ :where([data-number-field-input-row]) {
21
+ display: inline-flex;
22
+ align-items: stretch;
23
+ border: 1px solid var(--flx-ui-color-border);
24
+ border-radius: var(--flx-ui-radius-md);
25
+ overflow: hidden;
26
+ }
27
+ :where([data-number-field-input]) {
28
+ width: 5rem;
29
+ padding: var(--flx-ui-space-2) var(--flx-ui-space-2);
30
+ font-size: var(--flx-ui-font-size-sm);
31
+ font-family: inherit;
32
+ color: var(--flx-ui-color-fg);
33
+ background: transparent;
34
+ border: none;
35
+ outline: none;
36
+ text-align: center;
37
+ -moz-appearance: textfield;
38
+ }
39
+ :where([data-number-field-input]::-webkit-inner-spin-button),
40
+ :where([data-number-field-input]::-webkit-outer-spin-button) {
41
+ -webkit-appearance: none;
42
+ }
43
+ :where([data-number-field-input]:focus-visible) {
44
+ outline: var(--flx-ui-ctl-ring);
45
+ outline-offset: -2px;
46
+ border-radius: 0;
47
+ }
48
+ :where([data-number-field-input][disabled]) {
49
+ color: var(--flx-ui-color-fg-subtle);
50
+ }
51
+ :where([data-number-field-decrement]),
52
+ :where([data-number-field-increment]) {
53
+ display: inline-flex;
54
+ align-items: center;
55
+ justify-content: center;
56
+ width: 2rem;
57
+ padding: 0;
58
+ background: transparent;
59
+ border: none;
60
+ border-radius: 0;
61
+ color: var(--flx-ui-color-fg-muted);
62
+ cursor: pointer;
63
+ font-size: var(--flx-ui-font-size-sm);
64
+ transition: background var(--flx-ui-duration-fast) var(--flx-ui-easing-standard),
65
+ color var(--flx-ui-duration-fast) var(--flx-ui-easing-standard);
66
+ }
67
+ :where([data-number-field-decrement]) {
68
+ border-right: 1px solid var(--flx-ui-color-border);
69
+ }
70
+ :where([data-number-field-increment]) {
71
+ border-left: 1px solid var(--flx-ui-color-border);
72
+ }
73
+ :where([data-number-field-decrement]:hover:not([disabled])),
74
+ :where([data-number-field-increment]:hover:not([disabled])) {
75
+ background: var(--flx-ui-color-bg-subtle);
76
+ color: var(--flx-ui-color-fg);
77
+ }
78
+ :where([data-number-field-decrement][disabled]),
79
+ :where([data-number-field-increment][disabled]) {
80
+ color: var(--flx-ui-color-fg-subtle);
81
+ cursor: not-allowed;
82
+ }
83
+ }