@xenosystem/elements-react 0.0.1 → 0.0.3

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 (49) hide show
  1. package/LICENSE +18 -18
  2. package/README.md +41 -41
  3. package/package.json +58 -58
  4. package/src/chrome-separated.css +132 -132
  5. package/src/chrome-unified.css +72 -72
  6. package/src/containers/Badges.css +116 -116
  7. package/src/containers/avatar.css +70 -70
  8. package/src/containers/card.css +75 -75
  9. package/src/containers/chip.css +103 -103
  10. package/src/containers/list-row.css +132 -132
  11. package/src/containers/message-bubble.css +112 -112
  12. package/src/containers/panel.css +120 -120
  13. package/src/containers/table.css +102 -102
  14. package/src/containers/tile.css +17 -17
  15. package/src/content/callout.css +77 -77
  16. package/src/content/caret.css +30 -30
  17. package/src/content/code-block.css +159 -159
  18. package/src/content/collapsible.css +80 -80
  19. package/src/content/inline-code.css +14 -14
  20. package/src/content/model-picker.css +219 -219
  21. package/src/content/source-card.css +99 -99
  22. package/src/content/sources-disclosure.css +118 -118
  23. package/src/controls.css +453 -453
  24. package/src/fonts.css +34 -34
  25. package/src/forms/Checkbox.css +114 -114
  26. package/src/forms/RadioGroup.css +115 -115
  27. package/src/goo.css +97 -97
  28. package/src/icon-motion.css +1909 -1909
  29. package/src/layout/ResizablePanel.css +79 -79
  30. package/src/nav/sidebar.css +296 -296
  31. package/src/overlays/date-time-picker.css +238 -238
  32. package/src/overlays/menu.css +195 -195
  33. package/src/overlays/modal.css +167 -167
  34. package/src/overlays/picker-field.css +81 -81
  35. package/src/overlays/pill-filter.css +129 -129
  36. package/src/overlays/reveal.css +56 -56
  37. package/src/overlays/segmented-control.css +185 -185
  38. package/src/overlays/tabs.css +143 -143
  39. package/src/overlays/tooltip.css +126 -126
  40. package/src/scrollbar.css +184 -184
  41. package/src/size.css +179 -179
  42. package/src/status/progress-bar.css +88 -88
  43. package/src/status/spinner.css +33 -33
  44. package/src/status/statusindicator.css +65 -65
  45. package/src/status/steptimeline.css +137 -137
  46. package/src/status/thinkingcube.css +75 -75
  47. package/src/xeno-element.css +60 -60
  48. package/src/xeno-elements.css +71 -71
  49. package/src/xeno-theme.css +99 -99
@@ -1,75 +1,75 @@
1
- /*
2
- * Card — the base surface box. Monochrome shell: background + hairline border + card radius; depth on the
3
- * `elevated` variant is the darker `--xeno-elevated` token plus one soft (hue-free) shadow. The hover-lift
4
- * is opt-in via `data-interactive` and its transform lives behind `prefers-reduced-motion` — the border
5
- * brighten still applies when motion is reduced. Only theme tokens; no literal colours (shadow excepted).
6
- */
7
- .xeno-card {
8
- display: flex;
9
- flex-direction: column;
10
- padding: var(--xeno-chrome-card-pad);
11
- background: var(--xeno-chrome-card-bg);
12
- border: var(--xeno-chrome-card-border);
13
- border-radius: var(--xeno-chrome-card-radius);
14
- color: var(--xeno-text);
15
- }
16
-
17
- .xeno-card[data-variant='elevated'] {
18
- background: var(--xeno-chrome-card-elevated-bg);
19
- box-shadow: var(--xeno-chrome-card-shadow);
20
- }
21
-
22
- /* Body — the default slot. min-width:0 keeps flex-row children from overflowing the card. */
23
- .xeno-card-body {
24
- min-width: 0;
25
- }
26
-
27
- /* Header — a titled slot split from the body by an inset hairline. */
28
- .xeno-card-header {
29
- margin-bottom: 11px;
30
- padding-bottom: 11px;
31
- border-bottom: var(--xeno-chrome-divider);
32
- color: var(--xeno-text);
33
- font-size: 14px;
34
- font-weight: 600;
35
- line-height: 1.2;
36
- }
37
-
38
- /* Footer — a trailing slot, mirror of the header, quieter text. */
39
- .xeno-card-footer {
40
- margin-top: 11px;
41
- padding-top: 11px;
42
- border-top: var(--xeno-chrome-divider);
43
- color: var(--xeno-muted);
44
- font-size: 13px;
45
- line-height: 1.3;
46
- }
47
-
48
- /* Interactive — the sole hover-lift XENO allows. Border brighten is safe without motion. */
49
- .xeno-card[data-interactive='true'] {
50
- cursor: pointer;
51
- }
52
- .xeno-card[data-interactive='true']:hover {
53
- border-color: var(--xeno-muted);
54
- }
55
-
56
- /* Strong lift — the artifact-card affordance: a bigger rise + one soft, hue-free shadow. The shadow is
57
- safe without motion, so it sits outside the reduced-motion guard; only the transform is gated. */
58
- .xeno-card[data-interactive='true'][data-lift='strong']:hover {
59
- box-shadow: var(--xeno-chrome-card-lift);
60
- }
61
-
62
- @media (prefers-reduced-motion: no-preference) {
63
- .xeno-card[data-interactive='true'] {
64
- transition:
65
- transform var(--xeno-dur) var(--xeno-ease),
66
- border-color var(--xeno-dur) ease,
67
- box-shadow var(--xeno-dur) ease;
68
- }
69
- .xeno-card[data-interactive='true']:hover {
70
- transform: translateY(-1px);
71
- }
72
- .xeno-card[data-interactive='true'][data-lift='strong']:hover {
73
- transform: translateY(-3px);
74
- }
75
- }
1
+ /*
2
+ * Card — the base surface box. Monochrome shell: background + hairline border + card radius; depth on the
3
+ * `elevated` variant is the darker `--xeno-elevated` token plus one soft (hue-free) shadow. The hover-lift
4
+ * is opt-in via `data-interactive` and its transform lives behind `prefers-reduced-motion` — the border
5
+ * brighten still applies when motion is reduced. Only theme tokens; no literal colours (shadow excepted).
6
+ */
7
+ .xeno-card {
8
+ display: flex;
9
+ flex-direction: column;
10
+ padding: var(--xeno-chrome-card-pad);
11
+ background: var(--xeno-chrome-card-bg);
12
+ border: var(--xeno-chrome-card-border);
13
+ border-radius: var(--xeno-chrome-card-radius);
14
+ color: var(--xeno-text);
15
+ }
16
+
17
+ .xeno-card[data-variant='elevated'] {
18
+ background: var(--xeno-chrome-card-elevated-bg);
19
+ box-shadow: var(--xeno-chrome-card-shadow);
20
+ }
21
+
22
+ /* Body — the default slot. min-width:0 keeps flex-row children from overflowing the card. */
23
+ .xeno-card-body {
24
+ min-width: 0;
25
+ }
26
+
27
+ /* Header — a titled slot split from the body by an inset hairline. */
28
+ .xeno-card-header {
29
+ margin-bottom: 11px;
30
+ padding-bottom: 11px;
31
+ border-bottom: var(--xeno-chrome-divider);
32
+ color: var(--xeno-text);
33
+ font-size: 14px;
34
+ font-weight: 600;
35
+ line-height: 1.2;
36
+ }
37
+
38
+ /* Footer — a trailing slot, mirror of the header, quieter text. */
39
+ .xeno-card-footer {
40
+ margin-top: 11px;
41
+ padding-top: 11px;
42
+ border-top: var(--xeno-chrome-divider);
43
+ color: var(--xeno-muted);
44
+ font-size: 13px;
45
+ line-height: 1.3;
46
+ }
47
+
48
+ /* Interactive — the sole hover-lift XENO allows. Border brighten is safe without motion. */
49
+ .xeno-card[data-interactive='true'] {
50
+ cursor: pointer;
51
+ }
52
+ .xeno-card[data-interactive='true']:hover {
53
+ border-color: var(--xeno-muted);
54
+ }
55
+
56
+ /* Strong lift — the artifact-card affordance: a bigger rise + one soft, hue-free shadow. The shadow is
57
+ safe without motion, so it sits outside the reduced-motion guard; only the transform is gated. */
58
+ .xeno-card[data-interactive='true'][data-lift='strong']:hover {
59
+ box-shadow: var(--xeno-chrome-card-lift);
60
+ }
61
+
62
+ @media (prefers-reduced-motion: no-preference) {
63
+ .xeno-card[data-interactive='true'] {
64
+ transition:
65
+ transform var(--xeno-dur) var(--xeno-ease),
66
+ border-color var(--xeno-dur) ease,
67
+ box-shadow var(--xeno-dur) ease;
68
+ }
69
+ .xeno-card[data-interactive='true']:hover {
70
+ transform: translateY(-1px);
71
+ }
72
+ .xeno-card[data-interactive='true'][data-lift='strong']:hover {
73
+ transform: translateY(-3px);
74
+ }
75
+ }
@@ -1,103 +1,103 @@
1
- /*
2
- * Chip — a small selectable/removable token. SQUARE (radius-control), 1px hairline border, monochrome
3
- * shell. State is driven by `data-*` on the root; size metrics arrive as inline `--xeno-h/-padx/-gap/
4
- * -font` vars set from the size tokens, so a size can never drift from the data. No literal colours.
5
- */
6
- .xeno-chip {
7
- display: inline-flex;
8
- align-items: center;
9
- height: var(--xeno-h, 32px);
10
- max-width: 100%;
11
- color: var(--xeno-text);
12
- background: transparent;
13
- border: 1px solid var(--xeno-border);
14
- border-radius: var(--xeno-radius-control);
15
- font-family: inherit;
16
- font-size: var(--xeno-font, 14px);
17
- font-weight: 500;
18
- line-height: 1;
19
- white-space: nowrap;
20
- user-select: none;
21
- overflow: hidden; /* clips inner hover fills to the rounded corners */
22
- }
23
- .xeno-chip[data-selection='on'] {
24
- background: var(--xeno-control);
25
- }
26
- .xeno-chip[data-availability='disabled'] {
27
- opacity: 0.4;
28
- }
29
-
30
- /* Body — the label region: a plain span, or a toggle <button> when selectable. */
31
- .xeno-chip-body {
32
- display: inline-flex;
33
- align-items: center;
34
- gap: var(--xeno-gap, 6px);
35
- height: 100%;
36
- min-width: 0;
37
- padding: 0 var(--xeno-padx, 12px);
38
- color: inherit;
39
- background: transparent;
40
- border: none;
41
- font: inherit;
42
- line-height: 1;
43
- }
44
- .xeno-chip[data-removable='true'] .xeno-chip-body {
45
- padding-right: var(--xeno-gap, 6px);
46
- }
47
- .xeno-chip-label {
48
- overflow: hidden;
49
- text-overflow: ellipsis;
50
- white-space: nowrap;
51
- }
52
-
53
- /* Selectable body toggles the selection axis (aria-pressed); hover/press are CSS, not attributes. */
54
- .xeno-chip-select {
55
- cursor: pointer;
56
- transition:
57
- background var(--xeno-dur) ease,
58
- color var(--xeno-dur) ease;
59
- }
60
- .xeno-chip-select:hover:not(:disabled) {
61
- background: var(--xeno-hover);
62
- }
63
- .xeno-chip-select:focus-visible {
64
- outline: none;
65
- box-shadow: inset 0 0 0 2px var(--xeno-border);
66
- }
67
- .xeno-chip-select:disabled {
68
- cursor: not-allowed;
69
- }
70
-
71
- /* Remove — a bare 'x' button; muted ink that resolves to text on hover. */
72
- .xeno-chip-remove {
73
- display: inline-flex;
74
- align-items: center;
75
- justify-content: center;
76
- height: 100%;
77
- padding: 0 6px;
78
- color: var(--xeno-muted);
79
- background: transparent;
80
- border: none;
81
- cursor: pointer;
82
- transition:
83
- background var(--xeno-dur) ease,
84
- color var(--xeno-dur) ease;
85
- }
86
- .xeno-chip-remove:hover:not(:disabled) {
87
- color: var(--xeno-text);
88
- background: var(--xeno-hover);
89
- }
90
- .xeno-chip-remove:focus-visible {
91
- outline: none;
92
- box-shadow: inset 0 0 0 2px var(--xeno-border);
93
- }
94
- .xeno-chip-remove:disabled {
95
- cursor: not-allowed;
96
- }
97
-
98
- @media (prefers-reduced-motion: reduce) {
99
- .xeno-chip-select,
100
- .xeno-chip-remove {
101
- transition: none;
102
- }
103
- }
1
+ /*
2
+ * Chip — a small selectable/removable token. SQUARE (radius-control), 1px hairline border, monochrome
3
+ * shell. State is driven by `data-*` on the root; size metrics arrive as inline `--xeno-h/-padx/-gap/
4
+ * -font` vars set from the size tokens, so a size can never drift from the data. No literal colours.
5
+ */
6
+ .xeno-chip {
7
+ display: inline-flex;
8
+ align-items: center;
9
+ height: var(--xeno-h, 32px);
10
+ max-width: 100%;
11
+ color: var(--xeno-text);
12
+ background: transparent;
13
+ border: 1px solid var(--xeno-border);
14
+ border-radius: var(--xeno-radius-control);
15
+ font-family: inherit;
16
+ font-size: var(--xeno-font, 14px);
17
+ font-weight: 500;
18
+ line-height: 1;
19
+ white-space: nowrap;
20
+ user-select: none;
21
+ overflow: hidden; /* clips inner hover fills to the rounded corners */
22
+ }
23
+ .xeno-chip[data-selection='on'] {
24
+ background: var(--xeno-control);
25
+ }
26
+ .xeno-chip[data-availability='disabled'] {
27
+ opacity: 0.4;
28
+ }
29
+
30
+ /* Body — the label region: a plain span, or a toggle <button> when selectable. */
31
+ .xeno-chip-body {
32
+ display: inline-flex;
33
+ align-items: center;
34
+ gap: var(--xeno-gap, 6px);
35
+ height: 100%;
36
+ min-width: 0;
37
+ padding: 0 var(--xeno-padx, 12px);
38
+ color: inherit;
39
+ background: transparent;
40
+ border: none;
41
+ font: inherit;
42
+ line-height: 1;
43
+ }
44
+ .xeno-chip[data-removable='true'] .xeno-chip-body {
45
+ padding-right: var(--xeno-gap, 6px);
46
+ }
47
+ .xeno-chip-label {
48
+ overflow: hidden;
49
+ text-overflow: ellipsis;
50
+ white-space: nowrap;
51
+ }
52
+
53
+ /* Selectable body toggles the selection axis (aria-pressed); hover/press are CSS, not attributes. */
54
+ .xeno-chip-select {
55
+ cursor: pointer;
56
+ transition:
57
+ background var(--xeno-dur) ease,
58
+ color var(--xeno-dur) ease;
59
+ }
60
+ .xeno-chip-select:hover:not(:disabled) {
61
+ background: var(--xeno-hover);
62
+ }
63
+ .xeno-chip-select:focus-visible {
64
+ outline: none;
65
+ box-shadow: inset 0 0 0 2px var(--xeno-border);
66
+ }
67
+ .xeno-chip-select:disabled {
68
+ cursor: not-allowed;
69
+ }
70
+
71
+ /* Remove — a bare 'x' button; muted ink that resolves to text on hover. */
72
+ .xeno-chip-remove {
73
+ display: inline-flex;
74
+ align-items: center;
75
+ justify-content: center;
76
+ height: 100%;
77
+ padding: 0 6px;
78
+ color: var(--xeno-muted);
79
+ background: transparent;
80
+ border: none;
81
+ cursor: pointer;
82
+ transition:
83
+ background var(--xeno-dur) ease,
84
+ color var(--xeno-dur) ease;
85
+ }
86
+ .xeno-chip-remove:hover:not(:disabled) {
87
+ color: var(--xeno-text);
88
+ background: var(--xeno-hover);
89
+ }
90
+ .xeno-chip-remove:focus-visible {
91
+ outline: none;
92
+ box-shadow: inset 0 0 0 2px var(--xeno-border);
93
+ }
94
+ .xeno-chip-remove:disabled {
95
+ cursor: not-allowed;
96
+ }
97
+
98
+ @media (prefers-reduced-motion: reduce) {
99
+ .xeno-chip-select,
100
+ .xeno-chip-remove {
101
+ transition: none;
102
+ }
103
+ }
@@ -1,132 +1,132 @@
1
- /*
2
- * ListRow — a selectable / actionable list row. CSS-first: the `selection` and `availability` axes
3
- * arrive as `data-*` attributes on the root; interaction (hover / press / focus) is pure CSS. Depth is
4
- * hairline borders only — monochrome shell, no coloured glow. Only the theme tokens from
5
- * `xeno-theme.css` are used; no literal colours.
6
- */
7
-
8
- .xeno-list-row {
9
- display: flex;
10
- align-items: center;
11
- gap: 12px;
12
- width: 100%;
13
- box-sizing: border-box;
14
- min-width: 0;
15
- margin: 0;
16
- padding: 8px 12px;
17
- color: var(--xeno-text);
18
- text-align: left;
19
- background: transparent;
20
- border: 1px solid transparent;
21
- border-radius: var(--xeno-radius-md);
22
- cursor: default;
23
- transition:
24
- background var(--xeno-dur) ease,
25
- border-color var(--xeno-dur) ease;
26
- }
27
-
28
- /* Reset the native <button> chrome so a button row reads exactly like a div row. */
29
- button.xeno-list-row {
30
- font: inherit;
31
- appearance: none;
32
- -webkit-appearance: none;
33
- }
34
-
35
- /* Interactive rows (a real button, or a div carrying role=button / role=option) invite the pointer. */
36
- button.xeno-list-row:not(:disabled),
37
- .xeno-list-row[role='button'],
38
- .xeno-list-row[role='option'] {
39
- cursor: pointer;
40
- }
41
-
42
- /* Hover — only interactive, enabled, unselected rows lift onto --xeno-hover. */
43
- button.xeno-list-row:not(:disabled):hover,
44
- .xeno-list-row[role='button']:not([aria-disabled='true']):hover,
45
- .xeno-list-row[role='option']:not([aria-disabled='true']):hover {
46
- background: var(--xeno-hover);
47
- }
48
-
49
- /* Selection wins over hover — a selected row stays on the solid control fill. */
50
- .xeno-list-row[data-selection='on'],
51
- button.xeno-list-row[data-selection='on']:not(:disabled):hover,
52
- .xeno-list-row[role='button'][data-selection='on']:hover,
53
- .xeno-list-row[role='option'][data-selection='on']:hover {
54
- background: var(--xeno-control);
55
- }
56
-
57
- .xeno-list-row:focus-visible {
58
- outline: none;
59
- box-shadow:
60
- 0 0 0 2px var(--xeno-canvas),
61
- 0 0 0 4px var(--xeno-border);
62
- }
63
-
64
- button.xeno-list-row:active:not(:disabled) {
65
- transform: scale(0.995);
66
- }
67
-
68
- .xeno-list-row:disabled,
69
- .xeno-list-row[data-availability='disabled'] {
70
- opacity: 0.4;
71
- cursor: not-allowed;
72
- }
73
-
74
- /* ── Slots ───────────────────────────────────────────────────────────────────────── */
75
-
76
- /* Leading — glyph / avatar. Fixed, never shrinks. Its own children keep their intrinsic colour. */
77
- .xeno-list-row-leading {
78
- display: inline-flex;
79
- align-items: center;
80
- justify-content: center;
81
- flex: none;
82
- color: var(--xeno-muted);
83
- }
84
-
85
- /* Middle — the truncation column. min-width:0 lets the title/subtitle ellipsize inside the flex row. */
86
- .xeno-list-row-body {
87
- display: flex;
88
- flex: 1 1 auto;
89
- flex-direction: column;
90
- gap: 2px;
91
- min-width: 0;
92
- }
93
-
94
- .xeno-list-row-title {
95
- overflow: hidden;
96
- font-size: 14px;
97
- font-weight: 500;
98
- line-height: 1.3;
99
- color: var(--xeno-text);
100
- white-space: nowrap;
101
- text-overflow: ellipsis;
102
- }
103
-
104
- .xeno-list-row-subtitle {
105
- overflow: hidden;
106
- font-size: 12px;
107
- font-weight: 400;
108
- line-height: 1.3;
109
- color: var(--xeno-muted);
110
- white-space: nowrap;
111
- text-overflow: ellipsis;
112
- }
113
-
114
- /* Trailing — meta / actions pinned to the end. Fixed, never shrinks. */
115
- .xeno-list-row-trailing {
116
- display: inline-flex;
117
- align-items: center;
118
- gap: 6px;
119
- flex: none;
120
- margin-left: auto;
121
- font-size: 12px;
122
- color: var(--xeno-muted);
123
- }
124
-
125
- @media (prefers-reduced-motion: reduce) {
126
- .xeno-list-row {
127
- transition: none;
128
- }
129
- button.xeno-list-row:active:not(:disabled) {
130
- transform: none;
131
- }
132
- }
1
+ /*
2
+ * ListRow — a selectable / actionable list row. CSS-first: the `selection` and `availability` axes
3
+ * arrive as `data-*` attributes on the root; interaction (hover / press / focus) is pure CSS. Depth is
4
+ * hairline borders only — monochrome shell, no coloured glow. Only the theme tokens from
5
+ * `xeno-theme.css` are used; no literal colours.
6
+ */
7
+
8
+ .xeno-list-row {
9
+ display: flex;
10
+ align-items: center;
11
+ gap: 12px;
12
+ width: 100%;
13
+ box-sizing: border-box;
14
+ min-width: 0;
15
+ margin: 0;
16
+ padding: 8px 12px;
17
+ color: var(--xeno-text);
18
+ text-align: left;
19
+ background: transparent;
20
+ border: 1px solid transparent;
21
+ border-radius: var(--xeno-radius-md);
22
+ cursor: default;
23
+ transition:
24
+ background var(--xeno-dur) ease,
25
+ border-color var(--xeno-dur) ease;
26
+ }
27
+
28
+ /* Reset the native <button> chrome so a button row reads exactly like a div row. */
29
+ button.xeno-list-row {
30
+ font: inherit;
31
+ appearance: none;
32
+ -webkit-appearance: none;
33
+ }
34
+
35
+ /* Interactive rows (a real button, or a div carrying role=button / role=option) invite the pointer. */
36
+ button.xeno-list-row:not(:disabled),
37
+ .xeno-list-row[role='button'],
38
+ .xeno-list-row[role='option'] {
39
+ cursor: pointer;
40
+ }
41
+
42
+ /* Hover — only interactive, enabled, unselected rows lift onto --xeno-hover. */
43
+ button.xeno-list-row:not(:disabled):hover,
44
+ .xeno-list-row[role='button']:not([aria-disabled='true']):hover,
45
+ .xeno-list-row[role='option']:not([aria-disabled='true']):hover {
46
+ background: var(--xeno-hover);
47
+ }
48
+
49
+ /* Selection wins over hover — a selected row stays on the solid control fill. */
50
+ .xeno-list-row[data-selection='on'],
51
+ button.xeno-list-row[data-selection='on']:not(:disabled):hover,
52
+ .xeno-list-row[role='button'][data-selection='on']:hover,
53
+ .xeno-list-row[role='option'][data-selection='on']:hover {
54
+ background: var(--xeno-control);
55
+ }
56
+
57
+ .xeno-list-row:focus-visible {
58
+ outline: none;
59
+ box-shadow:
60
+ 0 0 0 2px var(--xeno-canvas),
61
+ 0 0 0 4px var(--xeno-border);
62
+ }
63
+
64
+ button.xeno-list-row:active:not(:disabled) {
65
+ transform: scale(0.995);
66
+ }
67
+
68
+ .xeno-list-row:disabled,
69
+ .xeno-list-row[data-availability='disabled'] {
70
+ opacity: 0.4;
71
+ cursor: not-allowed;
72
+ }
73
+
74
+ /* ── Slots ───────────────────────────────────────────────────────────────────────── */
75
+
76
+ /* Leading — glyph / avatar. Fixed, never shrinks. Its own children keep their intrinsic colour. */
77
+ .xeno-list-row-leading {
78
+ display: inline-flex;
79
+ align-items: center;
80
+ justify-content: center;
81
+ flex: none;
82
+ color: var(--xeno-muted);
83
+ }
84
+
85
+ /* Middle — the truncation column. min-width:0 lets the title/subtitle ellipsize inside the flex row. */
86
+ .xeno-list-row-body {
87
+ display: flex;
88
+ flex: 1 1 auto;
89
+ flex-direction: column;
90
+ gap: 2px;
91
+ min-width: 0;
92
+ }
93
+
94
+ .xeno-list-row-title {
95
+ overflow: hidden;
96
+ font-size: 14px;
97
+ font-weight: 500;
98
+ line-height: 1.3;
99
+ color: var(--xeno-text);
100
+ white-space: nowrap;
101
+ text-overflow: ellipsis;
102
+ }
103
+
104
+ .xeno-list-row-subtitle {
105
+ overflow: hidden;
106
+ font-size: 12px;
107
+ font-weight: 400;
108
+ line-height: 1.3;
109
+ color: var(--xeno-muted);
110
+ white-space: nowrap;
111
+ text-overflow: ellipsis;
112
+ }
113
+
114
+ /* Trailing — meta / actions pinned to the end. Fixed, never shrinks. */
115
+ .xeno-list-row-trailing {
116
+ display: inline-flex;
117
+ align-items: center;
118
+ gap: 6px;
119
+ flex: none;
120
+ margin-left: auto;
121
+ font-size: 12px;
122
+ color: var(--xeno-muted);
123
+ }
124
+
125
+ @media (prefers-reduced-motion: reduce) {
126
+ .xeno-list-row {
127
+ transition: none;
128
+ }
129
+ button.xeno-list-row:active:not(:disabled) {
130
+ transform: none;
131
+ }
132
+ }