@tensor_1/tokens 2.17.0 → 2.19.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tensor_1/tokens",
3
- "version": "2.17.0",
3
+ "version": "2.19.0",
4
4
  "description": "UIx v2 design tokens — the --uix-* contract for house products (Tensor, POSx, SHOPx). One DTCG source -> CSS variables, Tailwind theme, and typed TS constants.",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -6,6 +6,18 @@
6
6
  .uix-comment__meta { font-size: var(--uix-text-meta); color: var(--uix-text-muted); margin-bottom: var(--uix-space-1); }
7
7
  .uix-comment__author { font-weight: 600; color: var(--uix-text); }
8
8
  .uix-mention { color: var(--uix-link); background: var(--uix-brand-muted); padding: 0 var(--uix-space-1); border-radius: var(--uix-radius-xs); font-weight: 500; }
9
+
10
+ /* System events (status changed, assigned) are chrome, not speech: no bubble, muted meta
11
+ size, so a thread reads as people talking with the product's record in between. */
12
+ .uix-comment--system .uix-comment__body { background: transparent; border: 0; padding: 0 var(--uix-space-3); color: var(--uix-text-muted); font-size: var(--uix-text-meta); }
13
+ .uix-comment--system .uix-comment__meta { margin-bottom: 0; }
14
+ .uix-comment--system .uix-comment__author { font-weight: 500; color: var(--uix-text-muted); }
15
+
16
+ /* Reply quote: the answered message, above the answer. Two lines at most — it is a
17
+ pointer back, not a copy — with a rule so it never reads as part of the reply. */
18
+ .uix-comment__reply { margin: 0 0 var(--uix-space-2); padding: var(--uix-space-1) var(--uix-space-2); border-left: 2px solid var(--uix-border-strong); color: var(--uix-text-muted); font-size: var(--uix-text-meta); }
19
+ .uix-comment__reply-label { display: block; color: var(--uix-text-hushed); font-size: var(--uix-text-eyebrow); }
20
+ .uix-comment__reply-body { display: -webkit-box; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
9
21
  .uix-composer { border: 1px solid var(--uix-border); border-radius: var(--uix-radius-md); background: var(--uix-surface); }
10
22
  .uix-composer:focus-within { border-color: var(--uix-ring); box-shadow: 0 0 0 3px var(--uix-brand-muted); }
11
23
  .uix-composer textarea { width: 100%; border: 0; background: transparent; resize: vertical; padding: var(--uix-space-3) var(--uix-space-3); color: var(--uix-text); font: inherit; min-height: 60px; }
@@ -23,8 +23,14 @@
23
23
  .uix-searchbar__wrap { position: relative; }
24
24
  .uix-searchbar__wrap > svg { position: absolute; top: 50%; left: 14px; z-index: var(--uix-z-raised); width: 17px; height: 17px; color: var(--uix-text-hushed); transform: translateY(-50%); pointer-events: none; }
25
25
 
26
- .uix-shortcut-grid { grid-column: 1 / -1; margin-top: var(--uix-space-4); display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--uix-space-2); }
27
- .uix-shortcut-grid .uix-btn { min-height: 42px; justify-content: flex-start; }
26
+ /* The grid fits ITSELF rather than counting columns: a fixed count is only safe for label
27
+ lengths you already know, and `1fr` takes an auto minimum, so nowrap button labels push
28
+ the track past the viewport (LD-11: 382 px of content at 320 px on the German portal).
29
+ `min(100%, 12rem)` lets the track shrink to the container on a phone — one full-width
30
+ column — while 12rem keeps a sensible shortcut width on a desktop. Labels wrap, which
31
+ is why the track can shrink at all: .uix-btn is nowrap by design. */
32
+ .uix-shortcut-grid { grid-column: 1 / -1; margin-top: var(--uix-space-4); display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr)); gap: var(--uix-space-2); }
33
+ .uix-shortcut-grid .uix-btn { min-height: 42px; justify-content: flex-start; white-space: normal; overflow-wrap: anywhere; text-align: left; }
28
34
 
29
35
  /* ── notice queue — rotating "important updates" banner ────────────────────── */
30
36
  .uix-notice { min-height: 48px; padding: var(--uix-space-2) var(--uix-space-4); display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: var(--uix-space-3); border-left: 2px solid var(--uix-text); background: var(--uix-bg-subtle); }
@@ -153,7 +159,6 @@
153
159
  /* stacked zones: the caret flips to point up at the stage above */
154
160
  .uix-rundown__item[data-selected="true"]::before { top: -23px; left: 22px; transform: rotate(45deg); border-bottom: 0; border-top: 1px solid var(--uix-border-strong); }
155
161
 
156
- .uix-shortcut-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
157
162
  }
158
163
 
159
164
  @media (max-width: 620px) {
@@ -174,7 +179,8 @@
174
179
  .uix-searchbar { grid-template-columns: 1fr; }
175
180
 
176
181
  .uix-page-intro { margin-top: var(--uix-space-5); gap: var(--uix-space-5); }
177
- .uix-shortcut-grid { grid-template-columns: 1fr 1fr; }
182
+ /* auto-fit already narrows the tracks; only the touch target grows here. Re-pinning a
183
+ column count would reintroduce LD-11. */
178
184
  .uix-shortcut-grid .uix-btn { min-height: 48px; }
179
185
 
180
186
  .uix-story-signal { top: 20px; left: 20px; }
@@ -34,7 +34,7 @@
34
34
  * • .uix-navitem, .uix-breadcrumbs a, .uix-section-link are already quiet by their own
35
35
  * design and need no class here.
36
36
  *
37
- * Four forms:
37
+ * Five forms:
38
38
  * 1 · `.uix-link--quiet` — the opt-in utility, for block links outside the registry.
39
39
  * 2 · TITLE / NAME SLOTS — quiet by default. `:not(.uix-btn)` so an anchor deliberately
40
40
  * styled as a button inside a title slot keeps its button treatment.
@@ -43,9 +43,14 @@
43
43
  * links skip the hover underline too — see table.css (the row hover is the cue).
44
44
  * 4 · CONTAINER ANCHORS — the anchor IS the kit block (`<a class="uix-card">`). Without
45
45
  * this the base `a { color }` tints every word in the block brand-blue.
46
+ * 5 · `.uix-cell-link` — a data cell's whole value, when the framework <Link> carries a
47
+ * class so form 3's `:not([class])` cannot reach it. Quiet at REST as well: form 3
48
+ * and table.css calm only the hover, so such links flipped from brand-blue to text
49
+ * colour under the pointer (LD-06/CM-45). Row tint + focus ring are the cues.
46
50
  * Depends only on --uix-* tokens. */
47
51
  @layer uix.components {
48
52
  .uix-link--quiet,
53
+ .uix-cell-link,
49
54
 
50
55
  /* 2 · title / name slots */
51
56
  :is(
@@ -83,6 +88,11 @@
83
88
  /* body-row links are excluded: table.css keeps them calm on hover (the row tint is the cue) */
84
89
  :is(.uix-table :is(thead, tfoot) td, .uix-dl dd) a:not([class]):is(:hover, :focus-visible) { text-decoration: underline; }
85
90
 
91
+ /* Form 5 states its hover/focus rather than relying on no rule matching, so the contract
92
+ is written down. base.css supplies the ring; the radius keeps it hugging the text. */
93
+ .uix-cell-link:is(:hover, :focus-visible) { color: inherit; text-decoration: none; }
94
+ .uix-cell-link:focus-visible { border-radius: var(--uix-radius-xs); }
95
+
86
96
  /* Container anchors never underline: the anchor wraps the whole block, so an underline
87
97
  would strike through every word in it. They take the kit's standard row-hover tint
88
98
  instead — the same --uix-bg-hover that .uix-list__item / .uix-inbox__item / .uix-cmdk__item
@@ -10,6 +10,31 @@
10
10
  .uix-prose li + li { margin-top: var(--uix-space-1); }
11
11
  .uix-prose code { font-family: var(--uix-font-mono); font-size: .9em; background: var(--uix-bg-subtle); padding: 1px var(--uix-space-2); border-radius: var(--uix-radius-xs); }
12
12
  .uix-prose blockquote { border-left: 3px solid var(--uix-border-strong); padding-left: var(--uix-space-3); color: var(--uix-text-muted); }
13
+ /* markdown constructs written by the rich-text editor (RTE-01) */
14
+ .uix-prose h5, .uix-prose h6 { font-size: var(--uix-text-body); font-weight: 600; }
15
+ .uix-prose ul { list-style: disc; }
16
+ .uix-prose ol { list-style: decimal; }
17
+ .uix-prose li > :where(ul, ol) { margin-top: var(--uix-space-1); }
18
+ .uix-prose del, .uix-prose s { color: var(--uix-text-muted); }
19
+ .uix-prose pre {
20
+ overflow-x: auto; padding: var(--uix-space-3); border-radius: var(--uix-radius-md); background: var(--uix-bg-subtle);
21
+ font-family: var(--uix-font-mono); font-size: var(--uix-text-meta); line-height: var(--uix-leading-body);
22
+ }
23
+ .uix-prose pre code { padding: 0; background: none; font-size: inherit; }
24
+ .uix-prose hr { border: 0; border-top: 1px solid var(--uix-border); }
25
+ .uix-prose img { max-width: 100%; height: auto; border-radius: var(--uix-radius-sm); vertical-align: middle; }
26
+ .uix-prose table { border-collapse: collapse; font-size: var(--uix-text-body); }
27
+ .uix-prose th, .uix-prose td { padding: var(--uix-space-2) var(--uix-space-3); border: 1px solid var(--uix-border); text-align: left; vertical-align: top; }
28
+ .uix-prose th { background: var(--uix-bg-subtle); color: var(--uix-text); font-weight: 600; }
29
+ .uix-prose :where(th, td) > p { margin: 0; color: inherit; }
30
+ .uix-markdown__table { max-width: 100%; overflow-x: auto; border-radius: var(--uix-radius-xs); }
31
+ .uix-markdown__table:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 2px; }
32
+ .uix-prose .uix-markdown__tasks { list-style: none; padding-left: 0; }
33
+ .uix-markdown__task { display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: var(--uix-space-2); align-items: baseline; }
34
+ .uix-markdown__task > input { margin: 0; accent-color: var(--uix-accent); }
35
+ .uix-markdown__task > input:disabled { opacity: 1; }
36
+ .uix-markdown__task > :where(ul, ol) { grid-column: 2; }
37
+ .uix-markdown__task[data-checked] > span { color: var(--uix-text-muted); }
13
38
 
14
39
  .uix-note { display: flex; gap: var(--uix-space-3); padding: var(--uix-space-3) var(--uix-space-4); border-radius: var(--uix-radius-md); border: 1px solid var(--uix-border); background: var(--uix-surface); }
15
40
  .uix-note__icon { flex: none; }
@@ -19,4 +19,33 @@
19
19
  .uix-emoji-picker { display: flex; flex-wrap: wrap; gap: var(--uix-space-1); width: 196px; }
20
20
  .uix-emoji-picker__btn { font-size: 18px; line-height: 1; padding: var(--uix-space-2); border: 0; background: transparent; border-radius: var(--uix-radius-sm); cursor: pointer; }
21
21
  .uix-emoji-picker__btn:hover { background: var(--uix-bg-hover); }
22
+ .uix-emoji-picker__btn:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: -2px; }
23
+ .uix-reaction:focus-visible, .uix-reaction-add:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 2px; }
24
+ .uix-reaction:disabled, .uix-reaction-add:disabled { opacity: .55; cursor: not-allowed; }
25
+ .uix-reaction > span[aria-hidden] { line-height: inherit; }
26
+
27
+ /* full picker (EmojiPicker, RTE-01): search, category jump bar, sectioned 8-column grid */
28
+ .uix-emoji-picker__anchor { display: inline-flex; }
29
+ .uix-emoji-picker-popover { padding: var(--uix-space-2); }
30
+ .uix-emoji-picker--full { display: flex; flex-direction: column; flex-wrap: nowrap; gap: var(--uix-space-2); width: 324px; max-width: calc(100vw - 32px); }
31
+ .uix-emoji-picker__nav { display: flex; gap: var(--uix-space-0); overflow-x: auto; scrollbar-width: none; }
32
+ .uix-emoji-picker__nav-btn {
33
+ flex: none; width: 32px; height: 28px; padding: 0; border: 0; border-radius: var(--uix-radius-sm);
34
+ background: transparent; font-size: var(--uix-text-body); cursor: pointer;
35
+ }
36
+ .uix-emoji-picker__nav-btn:hover { background: var(--uix-bg-hover); }
37
+ .uix-emoji-picker__nav-btn:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: -2px; }
38
+ .uix-emoji-picker__grid { height: 264px; overflow-y: auto; overscroll-behavior: contain; scroll-padding-top: var(--uix-space-6); }
39
+ .uix-emoji-picker__section { content-visibility: auto; contain-intrinsic-size: auto 15rem; }
40
+ .uix-emoji-picker__heading {
41
+ position: sticky; top: 0; z-index: var(--uix-z-raised); padding: var(--uix-space-1) var(--uix-space-1);
42
+ background: var(--uix-surface-2); color: var(--uix-text-muted);
43
+ font-size: var(--uix-text-eyebrow); font-weight: 600; letter-spacing: var(--uix-tracking-eyebrow); text-transform: uppercase;
44
+ }
45
+ .uix-emoji-picker__cells { display: grid; grid-template-columns: repeat(8, 1fr); gap: var(--uix-space-0); }
46
+ .uix-emoji-picker--full .uix-emoji-picker__btn { display: flex; align-items: center; justify-content: center; padding: var(--uix-space-1); }
47
+ /* same-origin fallback image for emoji the device cannot draw (emojiImageBaseUrl) */
48
+ .uix-emoji-img { display: inline-block; width: 1.2em; height: 1.2em; vertical-align: -.2em; object-fit: contain; }
49
+ .uix-emoji-picker__status { font-size: var(--uix-text-meta); color: var(--uix-text-muted); }
50
+ .uix-emoji-picker__status:empty { display: none; }
22
51
  }
@@ -18,4 +18,38 @@
18
18
  .uix-relationship-graph--compact .uix-relationship-graph__visual,.uix-relationship-graph--compact .uix-relationship-graph__visual svg { min-height:16rem; height:16rem; }
19
19
  .uix-relationship-graph--state { padding:var(--uix-space-8); border:1px dashed var(--uix-border-strong); border-radius:var(--uix-radius-lg); }
20
20
  @media (max-width:640px) { .uix-relationship-graph__list li { display:grid; } }
21
+
22
+ /* Layered mode (ADR-0002): HTML node buttons over an SVG edge layer, one transformed stage. */
23
+ .uix-relationship-graph--layered .uix-relationship-graph__toolbar button:disabled { opacity:.5; cursor:not-allowed; }
24
+ .uix-relationship-graph__viewport { position:relative; height:var(--uix-relationship-graph-height, 22rem); overflow:hidden; border:1px solid var(--uix-border); border-radius:var(--uix-radius-lg); background:var(--uix-bg-subtle); cursor:grab; touch-action:pan-y; }
25
+ .uix-relationship-graph__viewport:active { cursor:grabbing; }
26
+ .uix-relationship-graph__stage { position:absolute; top:0; left:0; transform-origin:0 0; }
27
+ .uix-relationship-graph__stage[data-animate] { transition:transform var(--uix-dur) var(--uix-ease-out); }
28
+ .uix-relationship-graph__edges { position:absolute; top:0; left:0; overflow:visible; pointer-events:none; }
29
+ .uix-relationship-graph__link path { fill:none; stroke:color-mix(in srgb, var(--uix-text) 28%, transparent); stroke-width:1.5px; }
30
+ .uix-relationship-graph__link polygon { fill:color-mix(in srgb, var(--uix-text) 45%, transparent); }
31
+ .uix-relationship-graph__link text { fill:var(--uix-text-muted); font-family:var(--uix-font-sans); font-size:var(--uix-text-eyebrow); paint-order:stroke; stroke:var(--uix-bg-subtle); stroke-width:4px; stroke-linejoin:round; }
32
+ .uix-relationship-graph__link[data-kind="lateral"] path, .uix-relationship-graph__link[data-kind="cycle"] path { stroke-dasharray:5 4; }
33
+ .uix-relationship-graph__link[data-emphasis] path { stroke:color-mix(in srgb, var(--uix-text) 55%, transparent); }
34
+ .uix-relationship-graph__link[data-active] path { stroke:var(--uix-accent); stroke-width:2.5px; }
35
+ .uix-relationship-graph__link[data-active] polygon { fill:var(--uix-accent); }
36
+ .uix-relationship-graph__link[data-active] text { fill:var(--uix-text); }
37
+ .uix-relationship-graph__link[data-conflicted] path { stroke:var(--uix-danger); }
38
+ .uix-relationship-graph__link[data-dimmed] { opacity:.35; }
39
+ .uix-relationship-graph__column { position:absolute; top:var(--uix-space-4); overflow:hidden; color:var(--uix-text-muted); font-size:var(--uix-text-meta); white-space:nowrap; text-overflow:ellipsis; text-transform:uppercase; letter-spacing:.04em; pointer-events:none; }
40
+ .uix-relationship-graph__item { position:absolute; display:flex; flex-direction:column; justify-content:center; gap:var(--uix-space-1); padding:var(--uix-space-2) var(--uix-space-3); border:1px solid var(--uix-border-strong); border-radius:var(--uix-radius-md); background:var(--uix-surface); color:var(--uix-text); font:inherit; text-align:left; cursor:pointer; transition:border-color var(--uix-dur-fast) var(--uix-ease-out), opacity var(--uix-dur-fast) var(--uix-ease-out); }
41
+ .uix-relationship-graph__item:hover { border-color:var(--uix-accent); }
42
+ .uix-relationship-graph__item:focus-visible { outline:2px solid var(--uix-ring); outline-offset:2px; }
43
+ .uix-relationship-graph__item[data-root] { border-width:2px; }
44
+ .uix-relationship-graph__item[data-selected], .uix-relationship-graph__item[data-highlighted] { border-color:var(--uix-accent); background:color-mix(in srgb, var(--uix-accent) 8%, var(--uix-surface)); }
45
+ .uix-relationship-graph__item[data-conflicted] { border-color:var(--uix-danger); border-style:dashed; }
46
+ .uix-relationship-graph__item[data-dimmed] { opacity:.45; }
47
+ .uix-relationship-graph__item[data-kind="cluster"] { border-style:dashed; background:var(--uix-surface-2); }
48
+ .uix-relationship-graph__item-label { min-width:0; display:-webkit-box; overflow:hidden; -webkit-box-orient:vertical; -webkit-line-clamp:2; overflow-wrap:anywhere; font-size:var(--uix-text-body); line-height:1.25; }
49
+ .uix-relationship-graph__item-meta { min-width:0; overflow:hidden; color:var(--uix-text-muted); font-size:var(--uix-text-meta); white-space:nowrap; text-overflow:ellipsis; }
50
+ .uix-relationship-graph__item-flag { position:absolute; top:-9px; right:var(--uix-space-2); padding:0 var(--uix-space-1); border:1px solid var(--uix-border); border-radius:var(--uix-radius-pill); background:var(--uix-surface); color:var(--uix-text-muted); font-size:var(--uix-text-meta); line-height:1.4; }
51
+ .uix-relationship-graph__collapse { position:absolute; height:22px; padding:0 var(--uix-space-2); border:1px dashed var(--uix-border-strong); border-radius:var(--uix-radius-pill); background:var(--uix-surface); color:var(--uix-text-muted); font-size:var(--uix-text-meta); cursor:pointer; }
52
+ .uix-relationship-graph__collapse:focus-visible { outline:2px solid var(--uix-ring); outline-offset:2px; }
53
+ .uix-relationship-graph--compact .uix-relationship-graph__item { padding:var(--uix-space-1) var(--uix-space-2); }
54
+ @media (forced-colors: active) { .uix-relationship-graph__link path { stroke:CanvasText; } .uix-relationship-graph__link polygon { fill:CanvasText; } .uix-relationship-graph__item { border-color:ButtonText; } }
21
55
  }
@@ -0,0 +1,157 @@
1
+ /* uix rich-text editor — markdown editing surface (.uix-prose), a roving-tabindex toolbar that
2
+ scrolls sideways on narrow screens, source mode, counter/status footer, link popover and the
3
+ :shortcode emoji list. Composes with .uix-composer for comment boxes. Depends only on --uix-* tokens. */
4
+ @layer uix.components {
5
+ .uix-rich-text {
6
+ display: flex; flex-direction: column; min-width: 0;
7
+ border: 1px solid var(--uix-border); border-radius: var(--uix-radius-md); background: var(--uix-surface);
8
+ transition: border-color var(--uix-dur-fast) var(--uix-ease-out), box-shadow var(--uix-dur-fast) var(--uix-ease-out);
9
+ }
10
+ .uix-rich-text:hover { border-color: var(--uix-border-strong); }
11
+ .uix-rich-text:focus-within { border-color: var(--uix-ring); box-shadow: 0 0 0 3px var(--uix-brand-muted); }
12
+ .uix-rich-text[data-invalid] { border-color: var(--uix-danger); }
13
+ .uix-rich-text[data-invalid]:focus-within { box-shadow: 0 0 0 3px var(--uix-danger-bg); }
14
+ .uix-rich-text[data-disabled] { opacity: .55; background: var(--uix-bg-subtle); }
15
+ .uix-rich-text[data-readonly] { background: var(--uix-bg-subtle); }
16
+ /* The fallback is a bare textarea: the textarea draws the box, not the wrapper. */
17
+ .uix-rich-text--fallback { border: 0; background: transparent; }
18
+ .uix-rich-text--fallback:focus-within { box-shadow: none; }
19
+
20
+ /* toolbar — one row; scrolls instead of wrapping so a narrow screen never loses a control */
21
+ .uix-rich-text__toolbar {
22
+ display: flex; align-items: center; gap: var(--uix-space-1); min-width: 0;
23
+ padding: var(--uix-space-1) var(--uix-space-2); border-bottom: 1px solid var(--uix-border);
24
+ overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: thin;
25
+ /* size to the container, never to the row of tools (keeps grid/flex parents from widening) */
26
+ contain: inline-size;
27
+ }
28
+ .uix-rich-text__group { display: flex; align-items: center; gap: var(--uix-space-0); flex: none; }
29
+ .uix-rich-text__group + .uix-rich-text__group { padding-left: var(--uix-space-1); border-left: 1px solid var(--uix-border); }
30
+ .uix-rich-text__group--end { margin-left: auto; }
31
+ .uix-rich-text__tool {
32
+ display: inline-flex; align-items: center; justify-content: center; flex: none;
33
+ width: 32px; height: 32px; padding: 0; border: 0; border-radius: var(--uix-radius-sm);
34
+ background: transparent; color: var(--uix-text-hushed); cursor: pointer;
35
+ transition: background-color var(--uix-dur-fast) var(--uix-ease-out), color var(--uix-dur-fast) var(--uix-ease-out);
36
+ }
37
+ .uix-rich-text__tool svg { width: var(--uix-icon-sm); height: var(--uix-icon-sm); }
38
+ .uix-rich-text__tool:hover { background: var(--uix-bg-hover); color: var(--uix-text); }
39
+ .uix-rich-text__tool:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: -2px; }
40
+ .uix-rich-text__tool[aria-pressed="true"] { background: var(--uix-brand-muted); color: var(--uix-accent-text); }
41
+ .uix-rich-text__tool:disabled { opacity: .4; cursor: not-allowed; background: transparent; }
42
+
43
+ /* editing surface — .uix-prose supplies the typography */
44
+ .uix-rich-text__surface { position: relative; display: flex; flex-direction: column; min-width: 0; }
45
+ .uix-rich-text__editor { display: contents; }
46
+ .uix-rich-text__content {
47
+ max-width: none; padding: var(--uix-space-3); outline: none; overflow-wrap: anywhere;
48
+ font-size: var(--uix-text-body); line-height: var(--uix-leading-body); cursor: text;
49
+ }
50
+ .uix-rich-text[data-disabled] .uix-rich-text__content { cursor: not-allowed; }
51
+ /* ProseMirror essentials (the editor runs with injectCSS off, so a strict style-src CSP holds) */
52
+ .uix-rich-text__content.ProseMirror {
53
+ position: relative; white-space: pre-wrap; white-space: break-spaces; word-wrap: break-word;
54
+ font-variant-ligatures: none; font-feature-settings: "liga" 0;
55
+ }
56
+ .uix-rich-text__content [contenteditable="false"] { white-space: normal; }
57
+ .uix-rich-text__content [contenteditable="false"] [contenteditable="true"],
58
+ .uix-rich-text__content pre { white-space: pre-wrap; }
59
+ .uix-rich-text__content img.ProseMirror-separator { display: inline; border: 0; margin: 0; width: 0; height: 0; }
60
+ .uix-rich-text__content .ProseMirror-gapcursor { display: none; position: absolute; margin: 0; pointer-events: none; }
61
+ .uix-rich-text__content .ProseMirror-gapcursor::after {
62
+ content: ""; display: block; position: absolute; top: -2px; width: 20px; border-top: 1px solid var(--uix-text);
63
+ animation: uix-rich-text-caret 1.1s steps(2, start) infinite;
64
+ }
65
+ .uix-rich-text__content.ProseMirror-focused .ProseMirror-gapcursor { display: block; }
66
+ .uix-rich-text__content.ProseMirror-hideselection *::selection { background: transparent; }
67
+ .uix-rich-text__content.ProseMirror-hideselection * { caret-color: transparent; }
68
+ .uix-rich-text__content .tableWrapper { overflow-x: auto; }
69
+ @keyframes uix-rich-text-caret { to { visibility: hidden; } }
70
+ .uix-rich-text__content--loading { color: var(--uix-text-muted); cursor: progress; }
71
+ .uix-rich-text__content--loading > .uix-prose { max-width: none; }
72
+ /* editing text is full contrast (the reading view keeps prose's hushed body) */
73
+ .uix-rich-text .uix-rich-text__content :is(p, li) { color: var(--uix-text); }
74
+ /* Editor headings stay h1–h3 in the DOM; they look like the viewer's demoted h3–h5. */
75
+ .uix-rich-text__content h1 { font-size: var(--uix-text-h2); line-height: var(--uix-leading-h2); }
76
+ .uix-rich-text__content h2 { font-size: var(--uix-text-h3); line-height: var(--uix-leading-h3); }
77
+ .uix-rich-text__content h3 { font-size: var(--uix-text-body); line-height: var(--uix-leading-body); }
78
+ .uix-rich-text__content .is-editor-empty:first-child::before {
79
+ content: attr(data-placeholder); float: left; height: 0; pointer-events: none; color: var(--uix-text-muted);
80
+ }
81
+ .uix-rich-text__content a { cursor: text; }
82
+ /* the glyph stays in the text for editing and copying; its fallback image is drawn beside it */
83
+ .uix-rich-text__emoji-text { font-size: 0; }
84
+ .uix-rich-text__content .ProseMirror-selectednode { outline: 2px solid var(--uix-ring); outline-offset: 2px; }
85
+ .uix-rich-text__content .selectedCell { background: var(--uix-row-selected-bg); }
86
+ .uix-rich-text__content table { table-layout: fixed; width: 100%; }
87
+ .uix-rich-text__content ul[data-type="taskList"] { list-style: none; padding-left: 0; }
88
+ .uix-rich-text__content ul[data-type="taskList"] li { display: flex; gap: var(--uix-space-2); align-items: flex-start; }
89
+ .uix-rich-text__content ul[data-type="taskList"] li > label { flex: none; padding-top: var(--uix-space-1); }
90
+ .uix-rich-text__content ul[data-type="taskList"] li > div { flex: 1 1 auto; min-width: 0; }
91
+ .uix-rich-text__content ul[data-type="taskList"] input { accent-color: var(--uix-accent); margin: 0; }
92
+ .uix-rich-text__image-link {
93
+ display: inline-flex; align-items: center; gap: var(--uix-space-1); padding: 0 var(--uix-space-2);
94
+ border: 1px dashed var(--uix-border-strong); border-radius: var(--uix-radius-sm);
95
+ color: var(--uix-link); font-size: var(--uix-text-meta);
96
+ }
97
+
98
+ /* source mode — the textarea sits flush inside the frame */
99
+ .uix-rich-text__source.uix-textarea {
100
+ border: 0; border-radius: 0; background: transparent; box-shadow: none;
101
+ font-family: var(--uix-font-mono); font-size: var(--uix-text-meta);
102
+ }
103
+ .uix-rich-text__source.uix-textarea:focus { box-shadow: none; }
104
+ .uix-rich-text--fallback .uix-rich-text__source.uix-textarea {
105
+ border: 1px solid var(--uix-border); border-radius: var(--uix-radius-md); background: var(--uix-surface);
106
+ font-family: inherit; font-size: var(--uix-text-body);
107
+ }
108
+ .uix-rich-text--fallback .uix-rich-text__source.uix-textarea:focus { box-shadow: 0 0 0 3px var(--uix-brand-muted); }
109
+
110
+ /* footer — status, counter, trailing actions */
111
+ .uix-rich-text__footer {
112
+ display: flex; align-items: center; justify-content: flex-end; gap: var(--uix-space-2); flex-wrap: wrap;
113
+ padding: 0 var(--uix-space-3) var(--uix-space-2);
114
+ }
115
+ .uix-rich-text__footer:not(:has(> :not(:empty))) { display: none; }
116
+ .uix-rich-text--fallback .uix-rich-text__footer { padding: var(--uix-space-1) 0 0; }
117
+ .uix-rich-text__status { margin-right: auto; font-size: var(--uix-text-meta); color: var(--uix-text-muted); }
118
+ .uix-rich-text__status:empty { display: none; }
119
+ .uix-rich-text__status[data-kind="error"] { color: var(--uix-danger-text); }
120
+ .uix-rich-text__counter { font-size: var(--uix-text-meta); color: var(--uix-text-muted); font-variant-numeric: tabular-nums; }
121
+ .uix-rich-text__counter[data-over] { color: var(--uix-danger-text); font-weight: 600; }
122
+
123
+ /* composer variant — the composer draws the box; the toolbar joins the action bar */
124
+ .uix-rich-text--composer { border-radius: var(--uix-radius-md); }
125
+ .uix-rich-text--composer.uix-rich-text:focus-within { box-shadow: 0 0 0 3px var(--uix-brand-muted); }
126
+ .uix-rich-text--composer .uix-rich-text__bar { align-items: center; justify-content: flex-start; min-width: 0; }
127
+ .uix-rich-text--composer .uix-rich-text__toolbar { flex: 1 1 auto; padding: 0; border-bottom: 0; }
128
+ .uix-rich-text--composer .uix-rich-text__group--end { margin-left: 0; }
129
+ .uix-rich-text--composer .uix-rich-text__counter,
130
+ .uix-rich-text--composer .uix-rich-text__status { flex: none; margin-right: 0; }
131
+ .uix-rich-text--composer .uix-rich-text__bar > :last-child:not(.uix-rich-text__toolbar) { margin-left: auto; }
132
+ .uix-rich-text--composer .uix-rich-text__source.uix-textarea { border: 0; box-shadow: none; }
133
+
134
+ /* link popover */
135
+ .uix-rich-text__link { width: 320px; max-width: calc(100vw - 32px); padding: var(--uix-space-3); }
136
+ .uix-rich-text__link-form { display: flex; flex-direction: column; gap: var(--uix-space-3); }
137
+ .uix-rich-text__link-actions { display: flex; justify-content: flex-end; gap: var(--uix-space-2); }
138
+
139
+ /* :shortcode suggestions */
140
+ .uix-rich-text__suggest {
141
+ position: fixed; z-index: var(--uix-z-dropdown); min-width: 220px; max-height: 280px; overflow-y: auto;
142
+ padding: var(--uix-space-1); border: 1px solid var(--uix-border); border-radius: var(--uix-radius-md);
143
+ background: var(--uix-surface-2); box-shadow: var(--uix-shadow-popover); color: var(--uix-text);
144
+ }
145
+ .uix-rich-text__suggest-item {
146
+ display: flex; align-items: center; gap: var(--uix-space-2); padding: var(--uix-space-1) var(--uix-space-2);
147
+ border-radius: var(--uix-radius-sm); cursor: pointer; font-size: var(--uix-text-body);
148
+ }
149
+ .uix-rich-text__suggest-item:hover { background: var(--uix-bg-hover); }
150
+ .uix-rich-text__suggest-item[aria-selected="true"] { background: var(--uix-brand-muted); color: var(--uix-accent-text); }
151
+ .uix-rich-text__suggest-emoji { font-size: var(--uix-text-h3); line-height: 1; }
152
+ .uix-rich-text__suggest-name { font-family: var(--uix-font-mono); font-size: var(--uix-text-meta); }
153
+
154
+ @media (prefers-reduced-motion: reduce) {
155
+ .uix-rich-text, .uix-rich-text__tool { transition: none; }
156
+ }
157
+ }
@@ -0,0 +1,18 @@
1
+ /* uix save status — inline saving → saved → failed feedback for autosave and inline edits,
2
+ * where a toast is too loud and silence leaves the user guessing (TENSOR LD-13). The text
3
+ * sits in a polite live region the React component keeps mounted in every state. Colour
4
+ * is never the only cue: each state has its own words. Depends only on --uix-* tokens. */
5
+ @layer uix.components {
6
+ .uix-save-status { display: inline-flex; align-items: center; gap: var(--uix-space-1); min-height: var(--uix-icon-sm); color: var(--uix-text-muted); font-size: var(--uix-text-meta); white-space: nowrap; }
7
+ .uix-save-status__icon { width: var(--uix-space-2); height: var(--uix-space-2); flex-shrink: 0; border-radius: var(--uix-radius-pill); }
8
+ .uix-save-status[data-state="idle"] .uix-save-status__icon { display: none; }
9
+
10
+ .uix-save-status[data-state="saving"] .uix-save-status__icon { border: 1px solid var(--uix-border-strong); border-top-color: transparent; animation: uix-spin var(--uix-dur-slower) linear infinite; }
11
+ .uix-save-status[data-state="saved"] .uix-save-status__icon { background: var(--uix-success); }
12
+ .uix-save-status[data-state="failed"] { color: var(--uix-danger-text); }
13
+ .uix-save-status[data-state="failed"] .uix-save-status__icon { background: var(--uix-danger); }
14
+
15
+ @media (prefers-reduced-motion: reduce) {
16
+ .uix-save-status[data-state="saving"] .uix-save-status__icon { animation: none; }
17
+ }
18
+ }
@@ -88,7 +88,10 @@
88
88
  color: var(--uix-text); font: inherit;
89
89
  display: inline-flex; align-items: center; gap: var(--uix-space-1); white-space: nowrap;
90
90
  }
91
- .uix-id-cell__btn:hover { color: var(--uix-accent); }
91
+ /* No accent flip on hover: the row tint plus the revealed ↗ arrow are the affordance, and
92
+ an id that turns brand-blue under the pointer reads as a state change on the record
93
+ (HAR-133). Keeps --uix-text; the focus ring below is the keyboard cue. */
94
+ .uix-id-cell__btn:hover { color: var(--uix-text); }
92
95
  .uix-id-cell__btn:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 2px; border-radius: var(--uix-radius-xs); }
93
96
  .uix-id-cell__arrow {
94
97
  width: var(--uix-icon-sm); height: var(--uix-icon-sm); flex-shrink: 0; color: var(--uix-text-muted);