@xenosystem/elements-react 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +18 -18
- package/README.md +41 -41
- package/dist/containers/Chip.js +1 -1
- package/dist/containers/Panel.js +1 -1
- package/package.json +58 -58
- package/src/chrome-separated.css +132 -132
- package/src/chrome-unified.css +72 -72
- package/src/containers/Badges.css +116 -116
- package/src/containers/avatar.css +70 -70
- package/src/containers/card.css +75 -75
- package/src/containers/chip.css +103 -103
- package/src/containers/list-row.css +132 -132
- package/src/containers/message-bubble.css +112 -112
- package/src/containers/panel.css +125 -120
- package/src/containers/table.css +102 -102
- package/src/containers/tile.css +17 -17
- package/src/content/callout.css +77 -77
- package/src/content/caret.css +30 -30
- package/src/content/code-block.css +159 -159
- package/src/content/collapsible.css +80 -80
- package/src/content/inline-code.css +14 -14
- package/src/content/model-picker.css +219 -219
- package/src/content/source-card.css +99 -99
- package/src/content/sources-disclosure.css +118 -118
- package/src/controls.css +453 -453
- package/src/fonts.css +34 -34
- package/src/forms/Checkbox.css +114 -114
- package/src/forms/RadioGroup.css +115 -115
- package/src/goo.css +97 -97
- package/src/icon-motion.css +1909 -1909
- package/src/layout/ResizablePanel.css +79 -79
- package/src/nav/sidebar.css +296 -296
- package/src/overlays/date-time-picker.css +238 -238
- package/src/overlays/menu.css +195 -195
- package/src/overlays/modal.css +167 -167
- package/src/overlays/picker-field.css +81 -81
- package/src/overlays/pill-filter.css +129 -129
- package/src/overlays/reveal.css +56 -56
- package/src/overlays/segmented-control.css +185 -185
- package/src/overlays/tabs.css +143 -143
- package/src/overlays/tooltip.css +126 -126
- package/src/scrollbar.css +184 -184
- package/src/size.css +179 -179
- package/src/status/progress-bar.css +88 -88
- package/src/status/spinner.css +33 -33
- package/src/status/statusindicator.css +65 -65
- package/src/status/steptimeline.css +137 -137
- package/src/status/thinkingcube.css +75 -75
- package/src/xeno-element.css +60 -60
- package/src/xeno-elements.css +71 -71
- package/src/xeno-theme.css +112 -99
|
@@ -1,112 +1,112 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* MessageBubble — the chat message container. Monochrome SHELL: the container carries no colour of its
|
|
3
|
-
* own; only `children` may bring content colour. A `user` turn is a right-aligned bubble; an `assistant`
|
|
4
|
-
* turn is a bare left-aligned prose block (the locked design). State rides the root as `data-role` and,
|
|
5
|
-
* while a turn is still being written, `data-streaming`. Only the theme custom properties are used here.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
.xeno-message {
|
|
9
|
-
display: flex;
|
|
10
|
-
flex-direction: column;
|
|
11
|
-
gap: 6px;
|
|
12
|
-
max-width: 100%;
|
|
13
|
-
/* How wide a user bubble may run before it wraps. A knob rather than a prop, because the answer is
|
|
14
|
-
usually a MEDIA QUERY — a bubble that reads well at 80% on a desktop column is cramped at 80% of
|
|
15
|
-
a phone — and a breakpoint belongs in the product's stylesheet, not threaded through React state.
|
|
16
|
-
Override it on the turn, on the transcript, or in an `@media` block; the default stands alone. */
|
|
17
|
-
--xeno-message-max: 80%;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/* Sides: user hugs the right rail, assistant the left. */
|
|
21
|
-
.xeno-message[data-role='user'] {
|
|
22
|
-
align-items: flex-end;
|
|
23
|
-
}
|
|
24
|
-
.xeno-message[data-role='assistant'] {
|
|
25
|
-
align-items: flex-start;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
.xeno-message-body {
|
|
29
|
-
min-width: 0;
|
|
30
|
-
color: var(--xeno-text);
|
|
31
|
-
font-size: 15px;
|
|
32
|
-
line-height: 1.55;
|
|
33
|
-
overflow-wrap: anywhere;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/* Attachments — what was sent WITH the message, above the body and outside the bubble. They share the
|
|
37
|
-
turn's alignment and take no fill of their own: an image already has edges, and wrapping it in a
|
|
38
|
-
border plus 12px of surface only draws a second frame around the first. */
|
|
39
|
-
.xeno-message-attachments {
|
|
40
|
-
display: flex;
|
|
41
|
-
flex-wrap: wrap;
|
|
42
|
-
gap: 6px;
|
|
43
|
-
max-width: var(--xeno-message-max);
|
|
44
|
-
min-width: 0;
|
|
45
|
-
}
|
|
46
|
-
.xeno-message[data-role='user'] .xeno-message-attachments {
|
|
47
|
-
justify-content: flex-end;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/* User: a bubble — surface fill, hairline border, card radius with the bottom-right corner squared. */
|
|
51
|
-
.xeno-message[data-role='user'] .xeno-message-body {
|
|
52
|
-
max-width: var(--xeno-message-max);
|
|
53
|
-
/* A person's own words keep the shape they were typed in — the blank line they left between two
|
|
54
|
-
thoughts is part of the message. Only on this side: an assistant turn is rendered prose, where
|
|
55
|
-
the source newlines are markup and honouring them would double every paragraph break. */
|
|
56
|
-
white-space: pre-wrap;
|
|
57
|
-
padding: 12px;
|
|
58
|
-
background: var(--xeno-surface);
|
|
59
|
-
border: 1px solid var(--xeno-border);
|
|
60
|
-
border-radius: var(--xeno-radius-card);
|
|
61
|
-
border-bottom-right-radius: var(--xeno-radius-xs);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/* Assistant: no bubble, just prose (locked design). */
|
|
65
|
-
.xeno-message[data-role='assistant'] .xeno-message-body {
|
|
66
|
-
max-width: 100%;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/* Meta row: timestamp + hover action bar, aligned under the turn. */
|
|
70
|
-
.xeno-message-meta {
|
|
71
|
-
display: flex;
|
|
72
|
-
align-items: center;
|
|
73
|
-
gap: 8px;
|
|
74
|
-
min-height: 20px;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
.xeno-message-time {
|
|
78
|
-
font-size: 12px;
|
|
79
|
-
line-height: 1;
|
|
80
|
-
color: var(--xeno-muted);
|
|
81
|
-
font-variant-numeric: tabular-nums;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/* Hover action bar — fades in on hover/focus of the whole turn. */
|
|
85
|
-
.xeno-message-actions {
|
|
86
|
-
display: inline-flex;
|
|
87
|
-
align-items: center;
|
|
88
|
-
gap: 2px;
|
|
89
|
-
opacity: 0;
|
|
90
|
-
/* Transparent is not gone. At `opacity: 0` these buttons still take the pointer, so the strip of
|
|
91
|
-
nothing under a message swallowed clicks and — worse — sometimes ran Copy or Regenerate on a turn
|
|
92
|
-
whose bar the reader could not see. Faded out means out of reach; hover puts it back. */
|
|
93
|
-
pointer-events: none;
|
|
94
|
-
transition: opacity 0.18s var(--xeno-ease);
|
|
95
|
-
}
|
|
96
|
-
.xeno-message:hover .xeno-message-actions,
|
|
97
|
-
.xeno-message:focus-within .xeno-message-actions {
|
|
98
|
-
opacity: 1;
|
|
99
|
-
pointer-events: auto;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/* While streaming the bar stays hidden and inert, even on hover/focus (this rule wins by source order). */
|
|
103
|
-
.xeno-message[data-streaming] .xeno-message-actions {
|
|
104
|
-
opacity: 0;
|
|
105
|
-
pointer-events: none;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
@media (prefers-reduced-motion: reduce) {
|
|
109
|
-
.xeno-message-actions {
|
|
110
|
-
transition: none;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
1
|
+
/*
|
|
2
|
+
* MessageBubble — the chat message container. Monochrome SHELL: the container carries no colour of its
|
|
3
|
+
* own; only `children` may bring content colour. A `user` turn is a right-aligned bubble; an `assistant`
|
|
4
|
+
* turn is a bare left-aligned prose block (the locked design). State rides the root as `data-role` and,
|
|
5
|
+
* while a turn is still being written, `data-streaming`. Only the theme custom properties are used here.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
.xeno-message {
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
gap: 6px;
|
|
12
|
+
max-width: 100%;
|
|
13
|
+
/* How wide a user bubble may run before it wraps. A knob rather than a prop, because the answer is
|
|
14
|
+
usually a MEDIA QUERY — a bubble that reads well at 80% on a desktop column is cramped at 80% of
|
|
15
|
+
a phone — and a breakpoint belongs in the product's stylesheet, not threaded through React state.
|
|
16
|
+
Override it on the turn, on the transcript, or in an `@media` block; the default stands alone. */
|
|
17
|
+
--xeno-message-max: 80%;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* Sides: user hugs the right rail, assistant the left. */
|
|
21
|
+
.xeno-message[data-role='user'] {
|
|
22
|
+
align-items: flex-end;
|
|
23
|
+
}
|
|
24
|
+
.xeno-message[data-role='assistant'] {
|
|
25
|
+
align-items: flex-start;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.xeno-message-body {
|
|
29
|
+
min-width: 0;
|
|
30
|
+
color: var(--xeno-text);
|
|
31
|
+
font-size: 15px;
|
|
32
|
+
line-height: 1.55;
|
|
33
|
+
overflow-wrap: anywhere;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* Attachments — what was sent WITH the message, above the body and outside the bubble. They share the
|
|
37
|
+
turn's alignment and take no fill of their own: an image already has edges, and wrapping it in a
|
|
38
|
+
border plus 12px of surface only draws a second frame around the first. */
|
|
39
|
+
.xeno-message-attachments {
|
|
40
|
+
display: flex;
|
|
41
|
+
flex-wrap: wrap;
|
|
42
|
+
gap: 6px;
|
|
43
|
+
max-width: var(--xeno-message-max);
|
|
44
|
+
min-width: 0;
|
|
45
|
+
}
|
|
46
|
+
.xeno-message[data-role='user'] .xeno-message-attachments {
|
|
47
|
+
justify-content: flex-end;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* User: a bubble — surface fill, hairline border, card radius with the bottom-right corner squared. */
|
|
51
|
+
.xeno-message[data-role='user'] .xeno-message-body {
|
|
52
|
+
max-width: var(--xeno-message-max);
|
|
53
|
+
/* A person's own words keep the shape they were typed in — the blank line they left between two
|
|
54
|
+
thoughts is part of the message. Only on this side: an assistant turn is rendered prose, where
|
|
55
|
+
the source newlines are markup and honouring them would double every paragraph break. */
|
|
56
|
+
white-space: pre-wrap;
|
|
57
|
+
padding: 12px;
|
|
58
|
+
background: var(--xeno-surface);
|
|
59
|
+
border: 1px solid var(--xeno-border);
|
|
60
|
+
border-radius: var(--xeno-radius-card);
|
|
61
|
+
border-bottom-right-radius: var(--xeno-radius-xs);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* Assistant: no bubble, just prose (locked design). */
|
|
65
|
+
.xeno-message[data-role='assistant'] .xeno-message-body {
|
|
66
|
+
max-width: 100%;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* Meta row: timestamp + hover action bar, aligned under the turn. */
|
|
70
|
+
.xeno-message-meta {
|
|
71
|
+
display: flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
gap: 8px;
|
|
74
|
+
min-height: 20px;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.xeno-message-time {
|
|
78
|
+
font-size: 12px;
|
|
79
|
+
line-height: 1;
|
|
80
|
+
color: var(--xeno-muted);
|
|
81
|
+
font-variant-numeric: tabular-nums;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* Hover action bar — fades in on hover/focus of the whole turn. */
|
|
85
|
+
.xeno-message-actions {
|
|
86
|
+
display: inline-flex;
|
|
87
|
+
align-items: center;
|
|
88
|
+
gap: 2px;
|
|
89
|
+
opacity: 0;
|
|
90
|
+
/* Transparent is not gone. At `opacity: 0` these buttons still take the pointer, so the strip of
|
|
91
|
+
nothing under a message swallowed clicks and — worse — sometimes ran Copy or Regenerate on a turn
|
|
92
|
+
whose bar the reader could not see. Faded out means out of reach; hover puts it back. */
|
|
93
|
+
pointer-events: none;
|
|
94
|
+
transition: opacity 0.18s var(--xeno-ease);
|
|
95
|
+
}
|
|
96
|
+
.xeno-message:hover .xeno-message-actions,
|
|
97
|
+
.xeno-message:focus-within .xeno-message-actions {
|
|
98
|
+
opacity: 1;
|
|
99
|
+
pointer-events: auto;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* While streaming the bar stays hidden and inert, even on hover/focus (this rule wins by source order). */
|
|
103
|
+
.xeno-message[data-streaming] .xeno-message-actions {
|
|
104
|
+
opacity: 0;
|
|
105
|
+
pointer-events: none;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@media (prefers-reduced-motion: reduce) {
|
|
109
|
+
.xeno-message-actions {
|
|
110
|
+
transition: none;
|
|
111
|
+
}
|
|
112
|
+
}
|
package/src/containers/panel.css
CHANGED
|
@@ -1,120 +1,125 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Panel — a chrome stack: heading, body, footer.
|
|
3
|
-
*
|
|
4
|
-
* Every construction value comes from the `--xeno-chrome-*` tokens, so the SAME DOM renders as either
|
|
5
|
-
* chrome. Under `separated` (the default, required by DESIGN_SYSTEM.md §"Panel Structure") the wrapper
|
|
6
|
-
* paints the page colour and the three plates float on it with a 2px gap. Under `unified` the gap
|
|
7
|
-
* collapses to 0, the plates go transparent, the wrapper paints the surface and clips them, and the
|
|
8
|
-
* dividers come back — one card, exactly as this set rendered before the chrome axis existed.
|
|
9
|
-
*
|
|
10
|
-
* Nothing here knows a theme exists. That is the point: no component is built twice.
|
|
11
|
-
*
|
|
12
|
-
* Scrolling is chrome-independent — give the panel a bounded height and the body scrolls on its own
|
|
13
|
-
* (`min-height: 0` lets a flex child shrink below its content).
|
|
14
|
-
*/
|
|
15
|
-
.xeno-panel {
|
|
16
|
-
display: flex;
|
|
17
|
-
flex-direction: column;
|
|
18
|
-
gap: var(--xeno-chrome-gap);
|
|
19
|
-
padding: var(--xeno-chrome-inset);
|
|
20
|
-
min-height: 0;
|
|
21
|
-
background: var(--xeno-chrome-shell);
|
|
22
|
-
/* `separated` draws no border on the wrapper — the plates carry their own. `unified` needs one,
|
|
23
|
-
and gets it because the shell is painted and clipped. Both read the same token. */
|
|
24
|
-
border: 1px solid var(--xeno-chrome-border);
|
|
25
|
-
border-radius: var(--xeno-chrome-radius);
|
|
26
|
-
overflow: hidden;
|
|
27
|
-
color: var(--xeno-text);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/*
|
|
31
|
-
* Heading plate. §"Panel Structure" fixes it at 34px so headings line up with sidebar navigation
|
|
32
|
-
* across a product; the padding sets the text inside that rather than defining the height.
|
|
33
|
-
*/
|
|
34
|
-
.xeno-panel-header {
|
|
35
|
-
display: flex;
|
|
36
|
-
align-items: center;
|
|
37
|
-
justify-content: space-between;
|
|
38
|
-
gap: 12px;
|
|
39
|
-
flex: 0 0 auto;
|
|
40
|
-
min-height: var(--xeno-chrome-panel-header-min-h);
|
|
41
|
-
padding: var(--xeno-chrome-panel-header-pad);
|
|
42
|
-
background: var(--xeno-chrome-plate);
|
|
43
|
-
-webkit-backdrop-filter: var(--xeno-chrome-plate-filter);
|
|
44
|
-
backdrop-filter: var(--xeno-chrome-plate-filter);
|
|
45
|
-
border-bottom: var(--xeno-chrome-divider);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/*
|
|
49
|
-
* §"Headers and Footers": the header text is "small, uppercase, with letter-spacing. It functions as
|
|
50
|
-
* a label, not a headline." Matches the convention the code-block language chip already uses.
|
|
51
|
-
*/
|
|
52
|
-
.xeno-panel-title {
|
|
53
|
-
min-width: 0;
|
|
54
|
-
color: var(--xeno-chrome-title-color);
|
|
55
|
-
font-size: var(--xeno-chrome-title-size);
|
|
56
|
-
font-weight: 600;
|
|
57
|
-
letter-spacing: var(--xeno-chrome-title-spacing);
|
|
58
|
-
text-transform: var(--xeno-chrome-title-transform);
|
|
59
|
-
line-height: 1.2;
|
|
60
|
-
overflow: hidden;
|
|
61
|
-
text-overflow: ellipsis;
|
|
62
|
-
white-space: nowrap;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
.xeno-panel-actions {
|
|
66
|
-
display: inline-flex;
|
|
67
|
-
align-items: center;
|
|
68
|
-
gap: 6px;
|
|
69
|
-
flex: 0 0 auto;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/* Body plate — the scroll region. */
|
|
73
|
-
.xeno-panel-body {
|
|
74
|
-
flex: 1 1 auto;
|
|
75
|
-
min-height: 0;
|
|
76
|
-
padding: var(--xeno-chrome-panel-body-pad);
|
|
77
|
-
overflow: auto;
|
|
78
|
-
background: var(--xeno-chrome-body);
|
|
79
|
-
color: var(--xeno-text);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/*
|
|
83
|
-
* Footer plate — §"Headers and Footers": "Footer padding matches header padding exactly. Headers and
|
|
84
|
-
* footers are visually symmetric." Every box metric below is the header's, duplicated deliberately
|
|
85
|
-
* rather than shared through a common class: the symmetry is a design rule a future change must break
|
|
86
|
-
* on purpose, and a shared class would let one drift silently while the other did not.
|
|
87
|
-
*
|
|
88
|
-
* `space-between` gives the dismiss-left / confirm-right arrangement the products already use.
|
|
89
|
-
*/
|
|
90
|
-
.xeno-panel-footer {
|
|
91
|
-
display: flex;
|
|
92
|
-
align-items: center;
|
|
93
|
-
justify-content: space-between;
|
|
94
|
-
gap: 12px;
|
|
95
|
-
flex: 0 0 auto;
|
|
96
|
-
min-height: var(--xeno-chrome-panel-header-min-h);
|
|
97
|
-
padding: var(--xeno-chrome-panel-header-pad);
|
|
98
|
-
background: var(--xeno-chrome-plate);
|
|
99
|
-
border-top: var(--xeno-chrome-divider);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/*
|
|
103
|
-
* §4 "Radius Is Per-Corner, Not Per-Element": a corner is rounded only where the surface meets
|
|
104
|
-
* NOTHING. Each plate faces its neighbour across the 2px gap, so the facing corners are SQUARE — a
|
|
105
|
-
* uniform radius on every plate is the tell that this section was skipped, and it was.
|
|
106
|
-
*
|
|
107
|
-
* Declared on first/last child rather than per plate so a stack with no header, or no footer, still
|
|
108
|
-
* presents a rounded outer edge instead of a square one against the rounded shell.
|
|
109
|
-
*/
|
|
110
|
-
.xeno-panel > * {
|
|
111
|
-
border-radius: 0;
|
|
112
|
-
}
|
|
113
|
-
.xeno-panel > :first-child {
|
|
114
|
-
border-top-left-radius: var(--xeno-chrome-radius-plate);
|
|
115
|
-
border-top-right-radius: var(--xeno-chrome-radius-plate);
|
|
116
|
-
}
|
|
117
|
-
.xeno-panel > :last-child {
|
|
118
|
-
border-bottom-left-radius: var(--xeno-chrome-radius-plate);
|
|
119
|
-
border-bottom-right-radius: var(--xeno-chrome-radius-plate);
|
|
120
|
-
}
|
|
1
|
+
/*
|
|
2
|
+
* Panel — a chrome stack: heading, body, footer.
|
|
3
|
+
*
|
|
4
|
+
* Every construction value comes from the `--xeno-chrome-*` tokens, so the SAME DOM renders as either
|
|
5
|
+
* chrome. Under `separated` (the default, required by DESIGN_SYSTEM.md §"Panel Structure") the wrapper
|
|
6
|
+
* paints the page colour and the three plates float on it with a 2px gap. Under `unified` the gap
|
|
7
|
+
* collapses to 0, the plates go transparent, the wrapper paints the surface and clips them, and the
|
|
8
|
+
* dividers come back — one card, exactly as this set rendered before the chrome axis existed.
|
|
9
|
+
*
|
|
10
|
+
* Nothing here knows a theme exists. That is the point: no component is built twice.
|
|
11
|
+
*
|
|
12
|
+
* Scrolling is chrome-independent — give the panel a bounded height and the body scrolls on its own
|
|
13
|
+
* (`min-height: 0` lets a flex child shrink below its content).
|
|
14
|
+
*/
|
|
15
|
+
.xeno-panel {
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
gap: var(--xeno-chrome-gap);
|
|
19
|
+
padding: var(--xeno-chrome-inset);
|
|
20
|
+
min-height: 0;
|
|
21
|
+
background: var(--xeno-chrome-shell);
|
|
22
|
+
/* `separated` draws no border on the wrapper — the plates carry their own. `unified` needs one,
|
|
23
|
+
and gets it because the shell is painted and clipped. Both read the same token. */
|
|
24
|
+
border: 1px solid var(--xeno-chrome-border);
|
|
25
|
+
border-radius: var(--xeno-chrome-radius);
|
|
26
|
+
overflow: hidden;
|
|
27
|
+
color: var(--xeno-text);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/*
|
|
31
|
+
* Heading plate. §"Panel Structure" fixes it at 34px so headings line up with sidebar navigation
|
|
32
|
+
* across a product; the padding sets the text inside that rather than defining the height.
|
|
33
|
+
*/
|
|
34
|
+
.xeno-panel-header {
|
|
35
|
+
display: flex;
|
|
36
|
+
align-items: center;
|
|
37
|
+
justify-content: space-between;
|
|
38
|
+
gap: 12px;
|
|
39
|
+
flex: 0 0 auto;
|
|
40
|
+
min-height: var(--xeno-chrome-panel-header-min-h);
|
|
41
|
+
padding: var(--xeno-chrome-panel-header-pad);
|
|
42
|
+
background: var(--xeno-chrome-plate);
|
|
43
|
+
-webkit-backdrop-filter: var(--xeno-chrome-plate-filter);
|
|
44
|
+
backdrop-filter: var(--xeno-chrome-plate-filter);
|
|
45
|
+
border-bottom: var(--xeno-chrome-divider);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/*
|
|
49
|
+
* §"Headers and Footers": the header text is "small, uppercase, with letter-spacing. It functions as
|
|
50
|
+
* a label, not a headline." Matches the convention the code-block language chip already uses.
|
|
51
|
+
*/
|
|
52
|
+
.xeno-panel-title {
|
|
53
|
+
min-width: 0;
|
|
54
|
+
color: var(--xeno-chrome-title-color);
|
|
55
|
+
font-size: var(--xeno-chrome-title-size);
|
|
56
|
+
font-weight: 600;
|
|
57
|
+
letter-spacing: var(--xeno-chrome-title-spacing);
|
|
58
|
+
text-transform: var(--xeno-chrome-title-transform);
|
|
59
|
+
line-height: 1.2;
|
|
60
|
+
overflow: hidden;
|
|
61
|
+
text-overflow: ellipsis;
|
|
62
|
+
white-space: nowrap;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.xeno-panel-actions {
|
|
66
|
+
display: inline-flex;
|
|
67
|
+
align-items: center;
|
|
68
|
+
gap: 6px;
|
|
69
|
+
flex: 0 0 auto;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* Body plate — the scroll region. */
|
|
73
|
+
.xeno-panel-body {
|
|
74
|
+
flex: 1 1 auto;
|
|
75
|
+
min-height: 0;
|
|
76
|
+
padding: var(--xeno-chrome-panel-body-pad);
|
|
77
|
+
overflow: auto;
|
|
78
|
+
background: var(--xeno-chrome-body);
|
|
79
|
+
color: var(--xeno-text);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/*
|
|
83
|
+
* Footer plate — §"Headers and Footers": "Footer padding matches header padding exactly. Headers and
|
|
84
|
+
* footers are visually symmetric." Every box metric below is the header's, duplicated deliberately
|
|
85
|
+
* rather than shared through a common class: the symmetry is a design rule a future change must break
|
|
86
|
+
* on purpose, and a shared class would let one drift silently while the other did not.
|
|
87
|
+
*
|
|
88
|
+
* `space-between` gives the dismiss-left / confirm-right arrangement the products already use.
|
|
89
|
+
*/
|
|
90
|
+
.xeno-panel-footer {
|
|
91
|
+
display: flex;
|
|
92
|
+
align-items: center;
|
|
93
|
+
justify-content: space-between;
|
|
94
|
+
gap: 12px;
|
|
95
|
+
flex: 0 0 auto;
|
|
96
|
+
min-height: var(--xeno-chrome-panel-header-min-h);
|
|
97
|
+
padding: var(--xeno-chrome-panel-header-pad);
|
|
98
|
+
background: var(--xeno-chrome-plate);
|
|
99
|
+
border-top: var(--xeno-chrome-divider);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/*
|
|
103
|
+
* §4 "Radius Is Per-Corner, Not Per-Element": a corner is rounded only where the surface meets
|
|
104
|
+
* NOTHING. Each plate faces its neighbour across the 2px gap, so the facing corners are SQUARE — a
|
|
105
|
+
* uniform radius on every plate is the tell that this section was skipped, and it was.
|
|
106
|
+
*
|
|
107
|
+
* Declared on first/last child rather than per plate so a stack with no header, or no footer, still
|
|
108
|
+
* presents a rounded outer edge instead of a square one against the rounded shell.
|
|
109
|
+
*/
|
|
110
|
+
.xeno-panel > * {
|
|
111
|
+
border-radius: 0;
|
|
112
|
+
}
|
|
113
|
+
.xeno-panel > :first-child {
|
|
114
|
+
border-top-left-radius: var(--xeno-chrome-radius-plate);
|
|
115
|
+
border-top-right-radius: var(--xeno-chrome-radius-plate);
|
|
116
|
+
}
|
|
117
|
+
.xeno-panel > :last-child {
|
|
118
|
+
border-bottom-left-radius: var(--xeno-chrome-radius-plate);
|
|
119
|
+
border-bottom-right-radius: var(--xeno-chrome-radius-plate);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.xeno-panel-body:focus-visible {
|
|
123
|
+
outline: 1px solid var(--xeno-text);
|
|
124
|
+
outline-offset: -1px;
|
|
125
|
+
}
|