@atelic-action/ui 0.9.0 → 0.11.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/README.md CHANGED
@@ -41,7 +41,7 @@ The package sheets sit inside `@layer atelic-ui`, so any rule a site writes outs
41
41
  | Import | Exports |
42
42
  |---|---|
43
43
  | `@atelic-action/ui/chrome` | `SiteHeader`, `SiteMenu`, `Footer`, `CreditBar`, `StickyCTABar`, `BrandLockup`, `SkipLink`, and their prop types |
44
- | `@atelic-action/ui/components` | `NotFound` and the page components (`Checklist`, `DataTable`, `StatRow`, `Callout`, `Collapsible`, `StackedBar`, `ColumnChart`, `ThresholdScale`, `StarRating`), with their prop types (see [The Not Found Page](#the-not-found-page) and [Page Components](#page-components)) |
44
+ | `@atelic-action/ui/components` | `NotFound` and the page components (`Checklist`, `DataTable`, `StatRow`, `Callout`, `Collapsible`, `StackedBar`, `ColumnChart`, `ThresholdScale`, `StarRating`, `Disclosure`), with their prop types (see [The Not Found Page](#the-not-found-page) and [Page Components](#page-components)) |
45
45
  | `@atelic-action/ui/email` | The email components, the theme provider, the plain text helpers, and their prop types (see [Email](#email)) |
46
46
  | `@atelic-action/ui/email/render` | `renderEmail` and `renderFailureEmail`, the only entry that imports `react-dom/server` |
47
47
  | `@atelic-action/ui/tokens` | `Palette`, `atelicPalette`, `Fonts`, `atelicFonts`, `toThemeCSS`, `themeTokenMap` |
@@ -256,8 +256,9 @@ The blocks a page composes, each styled in `styles/components.css` inside the `a
256
256
  |---|---|---|
257
257
  | `Checklist` | A short list of checks, each passed or failed | Two columns when there is room, one on a phone. `passLabel` and `failLabel` name the icons for a screen reader |
258
258
  | `DataTable` | A table whose first column names each row, sortable by any column marked `sortable` | A click on a heading sorts on a wide screen. On a phone each row stacks, every fact wearing its column's label, and a dropdown sorts. A cell is a node, or `{ display, sort }` when what it shows is not what it sorts by; a formatted number in a string (`"$12,047"`, `"0.24%"`) sorts as a number. It renders in `defaultSort` on the server |
259
- | `StatRow` | A row of large figures, each over what it counts and its `period` | `trend` draws an arrow before a figure that is itself a change. `deltas` list comparisons beneath: an arrow, how far, against what (`basis`), and the earlier figure (`was`), each in its own tone, since a spend falling and a cost falling read differently |
259
+ | `StatRow` | A row of large figures, each over what it counts and its `period`. `variant="quiet"` is the glance row for a working page: a light figure in the text color over a small capitalized label, hairlines between; `unit` sets a figure's unit small beside it | `trend` draws an arrow before a figure that is itself a change. `deltas` list comparisons beneath: an arrow, how far, against what (`basis`), and the earlier figure (`was`), each in its own tone, since a spend falling and a cost falling read differently |
260
260
  | `Callout` | One finding: an icon in its tone beside the title, the explainer under it | `variant="box"` sets it apart on a tinted ground, for something missing or owed |
261
+ | `Disclosure` | A section the page opens and closes: a header row that folds a body the caller controls | For an app. The caller holds `open`, so it can remember what was folded, and a closed body is not rendered. The label is the toggle; `meta` (a count, a flag) sits beside it, outside its name, and a tap on it still folds. `headingLevel` makes the label a real heading |
261
262
  | `Collapsible` | A labeled line the reader opens for the detail behind it | A native `details`, so it works before any script loads and find in page opens it. `summary` carries the point, so the detail needs no explaining once open. It prints open |
262
263
  | `StackedBar` | One bar split into the parts of a whole, with a key | A part of zero is keyed and not drawn |
263
264
  | `ColumnChart` | A bar a period, the periods in groups that carry a label and a total | Months in quarters is the case it was cut for: a quarter reads against the one before it and the same one a year earlier |
@@ -266,8 +267,50 @@ The blocks a page composes, each styled in `styles/components.css` inside the `a
266
267
 
267
268
  **StarRating outside a site.** It is the one component that does not sit under `.mkt` or read a theme token: its styles are in `styles/star-rating.css`, and its two colors are the `--star-fill` and `--star-outline` custom properties, falling back to the text color and a faint tint of it. An app that installs the package for this component alone imports that one sheet, not `components.css`.
268
269
 
270
+ **A part's own color.** `StackedBar` takes a `color` on a segment, any CSS color, for parts that are not good or bad (the protein, carbs, and fat of a meal), and it wins over `tone`. `showKey={false}` draws the bar alone for a page that keys the parts its own way; the bar keeps its spoken summary.
271
+
269
272
  **Tones.** `good`, `warn`, `bad`, `info`, and `muted` are the one palette the package carries, because good, a warning, and broken read green, amber, and red whatever the brand is. A component sets `data-tone`, and `--tone`, `--tone-fill`, and `--tone-bg` follow for whatever sits inside. A site's theme may restate `--tone-good` and the rest on `.mkt`; `info` and `muted` come from the theme's own primary and neutral tokens.
270
273
 
274
+ ## In an App
275
+
276
+ The page components also work in an app that has its own base styles (Tailwind, shadcn) and wears none of the site chrome. Three things make that so (0.10.0):
277
+
278
+ 1. **A second scope.** Every rule in `styles/components.css` answers to `.atelic-ui` as well as `.mkt`. `.atelic-ui` has no resets behind it, so an app puts the class on the region that holds a package component and nothing else in it restyles. Prefer a region to `body`: the components use plain class names (`.stat .num`, `.callout`, `.note`), and an app with markup of its own under those names would have a page wide scope reach it. `body` is right only for an app that has checked it has none, and it is the one way to cover a component rendered in a portal.
279
+ 2. **The app imports one sheet**, `@atelic-action/ui/styles/components.css`, and neither `base.css` nor `chrome.css`. An app's own unlayered CSS always wins over the package. With Tailwind the app **must declare the layer order before any stylesheet loads**, in a `<style>` in the `<head>` of its `index.html`: `@layer properties, theme, base, atelic-ui, components, utilities;`. A layer ranks by where it is first named, and a bundler is free to name the package's first, which ranks it below Tailwind's reset; the reset then strips the padding, margins, and borders off every component, while colors and type still look right, so it is easy to miss. Named there, the package ranks above the reset and below the app's utilities.
280
+ 3. **The app maps its tokens** onto the ones the components read, in its own stylesheet on the same scope:
281
+
282
+ | Token | What it colors |
283
+ |---|---|
284
+ | `--ink`, `--ink-soft` | Text and quieter text |
285
+ | `--neutral-100`, `--neutral-200`, `--neutral-500` | Hairlines, tracks, and captions |
286
+ | `--surface-alt` | The quiet ground behind a muted tone |
287
+ | `--ui-primary` (falls back to `--primary`), `--primary-bright`, `--primary-tint` | The accent, its brighter fill, and its tint |
288
+ | `--ui-card` (falls back to `--card`) | A raised surface |
289
+ | `--font-sans`, `--font-mono`, `--radius-sm` | Type and the small radius |
290
+ | `--tone-good`, `--tone-warn`, `--tone-bad`, each with `-fill` and `-bg` | Optional: the package defines these, and a dark theme restates them |
291
+
292
+ `--ui-primary` and `--ui-card` exist because an app's own `--primary` and `--card` may hold something else: shadcn keeps bare HSL channels there, which are not a color on their own. A site sets neither and nothing changes.
293
+
294
+ ```css
295
+ /* app.css */
296
+ .atelic-ui {
297
+ --ink: hsl(var(--foreground));
298
+ --ink-soft: hsl(var(--muted-foreground));
299
+ --neutral-500: hsl(var(--muted-foreground));
300
+ --neutral-200: hsl(var(--border));
301
+ --neutral-100: hsl(var(--muted));
302
+ --surface-alt: hsl(var(--muted));
303
+ --ui-primary: hsl(var(--brand));
304
+ --primary-bright: hsl(var(--brand));
305
+ --primary-tint: color-mix(in srgb, hsl(var(--brand)) 12%, transparent);
306
+ --ui-card: hsl(var(--card));
307
+ }
308
+ ```
309
+
310
+ **Unit tests.** The package ships TSX source, and Vitest compiles it like any other module. A file under `node_modules` has no tsconfig of its own to name a JSX runtime, so a test that renders a package component fails with `React is not defined` until the test config says which to use: `esbuild: { jsx: "automatic" }` at the top level of `vitest.config.ts`. The app build needs nothing, since the React plugin sets it for Vite.
311
+
312
+ `StarRating` needs none of this: its sheet is unscoped and reads no token. The chrome (`SiteHeader`, `Footer`, and the rest) and `NotFound` remain site shaped and are not meant for a signed in app.
313
+
271
314
  ## The Gallery
272
315
 
273
316
  `gallery/` is one static page, The Components, that shows every page component on its own with made up data for a fictional bakery, wearing the practice's own palette and type. It lives at [ui.atelic.me](https://ui.atelic.me), noindexed and disallowed in its `robots.txt`, since it is for the people building with the package and never search inventory. It imports the components and stylesheets from `src/` by relative path rather than from npm, so it always shows the code in the repo, and it reads the package name and version from `package.json` at build time. It sits outside `src/`, so it is never published.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atelic-action/ui",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "Shared UI for the Atelic templates: site chrome, base styles, and the component library they install",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -0,0 +1,73 @@
1
+ import { ChevronDown } from "lucide-react";
2
+ import { type ReactNode, useId } from "react";
3
+
4
+ export interface DisclosureProps {
5
+ /** What the section is, and the toggle's accessible name. */
6
+ label: ReactNode;
7
+ open: boolean;
8
+ onToggle: () => void;
9
+ /**
10
+ * Sits beside the label and outside the toggle's name: a count, a flag, a
11
+ * small chart. A tap on it still folds the section.
12
+ */
13
+ meta?: ReactNode;
14
+ /** Makes the label a real heading at this level; leave it out for none. */
15
+ headingLevel?: 2 | 3 | 4 | 5 | 6;
16
+ /** The panel's id; minted when left out. */
17
+ panelId?: string;
18
+ className?: string;
19
+ children: ReactNode;
20
+ }
21
+
22
+ /**
23
+ * A section the page opens and closes: a header row that folds a body the
24
+ * caller controls. Where `Collapsible` is a native details element that keeps
25
+ * its own state and its detail in the page, this one is for an app. The
26
+ * caller holds `open`, so it can remember which sections were folded, and a
27
+ * closed body is not rendered, so a long list costs nothing until asked for.
28
+ *
29
+ * The label is the toggle, so its name reads "Produce, collapsed" and not
30
+ * "Produce 10 items"; `meta` is a sibling for that reason. The toggle's
31
+ * `::after` stretches over the whole header row, so a tap anywhere on it,
32
+ * meta and chevron included, folds the section.
33
+ */
34
+ export function Disclosure({
35
+ label,
36
+ open,
37
+ onToggle,
38
+ meta,
39
+ headingLevel,
40
+ panelId,
41
+ className,
42
+ children,
43
+ }: DisclosureProps) {
44
+ const minted = useId();
45
+ const id = panelId ?? minted;
46
+ const Root = headingLevel ? "section" : "div";
47
+ const Title = headingLevel ? (`h${headingLevel}` as const) : "div";
48
+
49
+ return (
50
+ <Root className={["disclosure", open && "is-open", className].filter(Boolean).join(" ")}>
51
+ <div className="disclosure-head">
52
+ <Title className="disclosure-title">
53
+ <button
54
+ type="button"
55
+ className="disclosure-toggle"
56
+ onClick={onToggle}
57
+ aria-expanded={open}
58
+ aria-controls={id}
59
+ >
60
+ {label}
61
+ </button>
62
+ </Title>
63
+ {meta != null && meta !== false && <span className="disclosure-meta">{meta}</span>}
64
+ <ChevronDown className="disclosure-chevron" size={16} aria-hidden="true" />
65
+ </div>
66
+ {open && (
67
+ <div className="disclosure-body" id={id}>
68
+ {children}
69
+ </div>
70
+ )}
71
+ </Root>
72
+ );
73
+ }
@@ -1,3 +1,4 @@
1
+ import type { CSSProperties } from "react";
1
2
  import type { Tone } from "./tone";
2
3
 
3
4
  export interface StackedBarSegment {
@@ -5,16 +6,26 @@ export interface StackedBarSegment {
5
6
  value: number;
6
7
  /** The figure as printed, e.g. "1,158"; defaults to the value with separators. */
7
8
  display?: string;
8
- tone: Tone;
9
+ /** How the part reads. Leave it out when `color` says it instead. */
10
+ tone?: Tone;
11
+ /** A color of the part's own, any CSS color, for parts that are not good or bad. It wins over `tone`. */
12
+ color?: string;
9
13
  }
10
14
 
11
15
  export interface StackedBarProps {
12
16
  segments: StackedBarSegment[];
17
+ /** False draws the bar alone, for a page that keys the parts its own way. The bar keeps its spoken summary. */
18
+ showKey?: boolean;
13
19
  className?: string;
14
20
  }
15
21
 
22
+ /** A part's own color, set as the fill its bar and its swatch both read. */
23
+ function fill(segment: StackedBarSegment): CSSProperties | undefined {
24
+ return segment.color ? ({ "--tone-fill": segment.color } as CSSProperties) : undefined;
25
+ }
26
+
16
27
  /** One bar split into the parts of a whole, with a key naming each part and its figure. */
17
- export function StackedBar({ segments, className }: StackedBarProps) {
28
+ export function StackedBar({ segments, showKey = true, className }: StackedBarProps) {
18
29
  const total = segments.reduce((sum, segment) => sum + segment.value, 0) || 1;
19
30
  const figure = (segment: StackedBarSegment) =>
20
31
  segment.display ?? segment.value.toLocaleString("en-US");
@@ -28,19 +39,21 @@ export function StackedBar({ segments, className }: StackedBarProps) {
28
39
  <span
29
40
  key={segment.label}
30
41
  data-tone={segment.tone}
31
- style={{ flexGrow: segment.value / total }}
42
+ style={{ flexGrow: segment.value / total, ...fill(segment) }}
32
43
  />
33
44
  ))}
34
45
  </div>
35
- <ul className="chart-key">
36
- {segments.map((segment) => (
37
- <li key={segment.label} data-tone={segment.tone}>
38
- <span className="chart-swatch" aria-hidden="true" />
39
- <b>{figure(segment)}</b>
40
- <span>{segment.label}</span>
41
- </li>
42
- ))}
43
- </ul>
46
+ {showKey && (
47
+ <ul className="chart-key">
48
+ {segments.map((segment) => (
49
+ <li key={segment.label} data-tone={segment.tone} style={fill(segment)}>
50
+ <span className="chart-swatch" aria-hidden="true" />
51
+ <b>{figure(segment)}</b>
52
+ <span>{segment.label}</span>
53
+ </li>
54
+ ))}
55
+ </ul>
56
+ )}
44
57
  </div>
45
58
  );
46
59
  }
@@ -22,6 +22,8 @@ export interface StatDelta {
22
22
  export interface StatItem {
23
23
  /** The big figure, e.g. "$199", "3,948", "23%"; may embed an inline glyph. */
24
24
  value: ReactNode;
25
+ /** The figure's unit, set small after it on the same baseline, e.g. "mi". */
26
+ unit?: ReactNode;
25
27
  /** An arrow before the figure, when the figure is itself a change. */
26
28
  trend?: StatDirection;
27
29
  /** What the figure counts. */
@@ -38,6 +40,13 @@ export interface StatItem {
38
40
 
39
41
  export interface StatRowProps {
40
42
  stats: StatItem[];
43
+ /**
44
+ * How loud the row is. `loud`, the default, is a report's headline figures:
45
+ * heavy, large, in the accent. `quiet` is a glance row for a working page:
46
+ * a light figure in the text color over a small capitalized label, the
47
+ * stats divided by a hairline. A tone still colors a quiet figure.
48
+ */
49
+ variant?: "loud" | "quiet";
41
50
  className?: string;
42
51
  }
43
52
 
@@ -50,9 +59,13 @@ const spoken = { up: "Up", down: "Down", flat: "Unchanged" } as const;
50
59
  * closes the stat, with the period it covers. The row wraps to as many
51
60
  * columns as fit, and arrows are read aloud as up or down.
52
61
  */
53
- export function StatRow({ stats, className }: StatRowProps) {
62
+ export function StatRow({ stats, variant = "loud", className }: StatRowProps) {
54
63
  return (
55
- <div className={["stat-row", className].filter(Boolean).join(" ")}>
64
+ <div
65
+ className={["stat-row", variant === "quiet" && "is-quiet", className]
66
+ .filter(Boolean)
67
+ .join(" ")}
68
+ >
56
69
  {stats.map((stat, i) => {
57
70
  const Trend = stat.trend ? arrow[stat.trend] : null;
58
71
  return (
@@ -63,6 +76,7 @@ export function StatRow({ stats, className }: StatRowProps) {
63
76
  <Trend className="stat-trend" role="img" aria-label={spoken[stat.trend]} />
64
77
  )}
65
78
  {stat.value}
79
+ {stat.unit != null && <span className="stat-unit">{stat.unit}</span>}
66
80
  </div>
67
81
  {stat.deltas?.map((delta) => {
68
82
  const Icon = arrow[delta.direction];
@@ -16,6 +16,7 @@ export {
16
16
  type DataTableValueCell,
17
17
  type SortDirection,
18
18
  } from "./DataTable";
19
+ export { Disclosure, type DisclosureProps } from "./Disclosure";
19
20
  export { NotFound, type NotFoundProps } from "./NotFound";
20
21
  export { StackedBar, type StackedBarProps, type StackedBarSegment } from "./StackedBar";
21
22
  export { describeStarRating, StarRating, type StarRatingProps } from "./StarRating";
@@ -2,21 +2,33 @@
2
2
 
3
3
  /* Layout defaults for the components under src/components. A site's own
4
4
  classes (.page-hero, .eyebrow, .lead, .btn) arrive unlayered and win, so
5
- these only fill in where a site does not style them. */
5
+ these only fill in where a site does not style them.
6
+
7
+ Every rule answers to either of two scopes. `.mkt` is a site's canvas,
8
+ which base.css also resets headings, links, and buttons under.
9
+ `.atelic-ui` is the same scope with no resets behind it, for an app that
10
+ has its own base (Tailwind, shadcn) and wants only the components. The
11
+ two weigh the same, so nothing a site already overrides changes.
12
+
13
+ Two tokens are read through names of the package's own first:
14
+ `--ui-primary` and `--ui-card`, each falling back to `--primary` and
15
+ `--card`. A site sets neither and nothing changes. An app whose own
16
+ `--primary` and `--card` hold something else (shadcn keeps bare HSL
17
+ channels there) sets the `--ui-` names and leaves its own alone. */
6
18
  @layer atelic-ui {
7
- .mkt .not-found-hero {
19
+ :is(.mkt, .atelic-ui) .not-found-hero {
8
20
  padding-top: calc(var(--nav-height, 72px) + clamp(40px, 7vw, 86px));
9
21
  padding-bottom: clamp(28px, 5vw, 60px);
10
22
  }
11
- .mkt .not-found-hero h1 {
23
+ :is(.mkt, .atelic-ui) .not-found-hero h1 {
12
24
  margin: 16px 0 0;
13
25
  }
14
- .mkt .not-found-hero .lead {
26
+ :is(.mkt, .atelic-ui) .not-found-hero .lead {
15
27
  margin-top: 22px;
16
28
  max-width: 56ch;
17
29
  }
18
- .mkt .not-found-links .cta-row,
19
- .mkt .not-found-closing .cta-row {
30
+ :is(.mkt, .atelic-ui) .not-found-links .cta-row,
31
+ :is(.mkt, .atelic-ui) .not-found-closing .cta-row {
20
32
  margin-top: 22px;
21
33
  }
22
34
 
@@ -24,7 +36,7 @@
24
36
  warning, and broken read green, amber, and red whatever the brand is.
25
37
  A site's theme may restate any of these on .mkt. A component sets
26
38
  data-tone, and --tone, --tone-fill, and --tone-bg follow. ---- */
27
- .mkt {
39
+ :is(.mkt, .atelic-ui) {
28
40
  --tone-good: #1b7f3b;
29
41
  --tone-good-fill: #37a35c;
30
42
  --tone-good-bg: #eaf6ee;
@@ -35,27 +47,27 @@
35
47
  --tone-bad-fill: #d9483f;
36
48
  --tone-bad-bg: #fdecea;
37
49
  }
38
- .mkt [data-tone="good"] {
50
+ :is(.mkt, .atelic-ui) [data-tone="good"] {
39
51
  --tone: var(--tone-good);
40
52
  --tone-fill: var(--tone-good-fill);
41
53
  --tone-bg: var(--tone-good-bg);
42
54
  }
43
- .mkt [data-tone="warn"] {
55
+ :is(.mkt, .atelic-ui) [data-tone="warn"] {
44
56
  --tone: var(--tone-warn);
45
57
  --tone-fill: var(--tone-warn-fill);
46
58
  --tone-bg: var(--tone-warn-bg);
47
59
  }
48
- .mkt [data-tone="bad"] {
60
+ :is(.mkt, .atelic-ui) [data-tone="bad"] {
49
61
  --tone: var(--tone-bad);
50
62
  --tone-fill: var(--tone-bad-fill);
51
63
  --tone-bg: var(--tone-bad-bg);
52
64
  }
53
- .mkt [data-tone="info"] {
54
- --tone: var(--primary);
65
+ :is(.mkt, .atelic-ui) [data-tone="info"] {
66
+ --tone: var(--ui-primary, var(--primary));
55
67
  --tone-fill: var(--primary-bright);
56
68
  --tone-bg: var(--primary-tint);
57
69
  }
58
- .mkt [data-tone="muted"] {
70
+ :is(.mkt, .atelic-ui) [data-tone="muted"] {
59
71
  --tone: var(--neutral-500);
60
72
  --tone-fill: var(--neutral-200);
61
73
  --tone-bg: var(--surface-alt);
@@ -63,50 +75,50 @@
63
75
 
64
76
  /* ---- StatRow: large figures, each over what it counts, its period,
65
77
  and its comparisons ---- */
66
- .mkt .stat-row {
78
+ :is(.mkt, .atelic-ui) .stat-row {
67
79
  display: grid;
68
80
  gap: 24px;
69
81
  grid-template-columns: repeat(2, minmax(0, 1fr));
70
82
  }
71
83
  @media (min-width: 760px) {
72
- .mkt .stat-row {
84
+ :is(.mkt, .atelic-ui) .stat-row {
73
85
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
74
86
  }
75
87
  }
76
- .mkt .stat-row.is-center .stat {
88
+ :is(.mkt, .atelic-ui) .stat-row.is-center .stat {
77
89
  text-align: center;
78
90
  }
79
- .mkt .stat .num {
91
+ :is(.mkt, .atelic-ui) .stat .num {
80
92
  display: flex;
81
93
  align-items: center;
82
94
  gap: 0.12em;
83
95
  font-size: clamp(2.2rem, 5vw, 3rem);
84
96
  font-weight: 800;
85
97
  letter-spacing: -0.03em;
86
- color: var(--tone, var(--primary));
98
+ color: var(--tone, var(--ui-primary, var(--primary)));
87
99
  line-height: 1;
88
100
  font-variant-numeric: tabular-nums;
89
101
  }
90
- .mkt .stat-row.is-center .stat .num {
102
+ :is(.mkt, .atelic-ui) .stat-row.is-center .stat .num {
91
103
  justify-content: center;
92
104
  }
93
- .mkt .stat-trend {
105
+ :is(.mkt, .atelic-ui) .stat-trend {
94
106
  width: 0.8em;
95
107
  height: 0.8em;
96
108
  flex: none;
97
109
  }
98
- .mkt .stat .lbl {
110
+ :is(.mkt, .atelic-ui) .stat .lbl {
99
111
  font-size: 0.95rem;
100
112
  font-weight: 650;
101
113
  color: var(--ink);
102
114
  margin-top: 8px;
103
115
  }
104
- .mkt .stat-period {
116
+ :is(.mkt, .atelic-ui) .stat-period {
105
117
  margin-left: 0.5em;
106
118
  font-weight: 400;
107
119
  color: var(--neutral-500);
108
120
  }
109
- .mkt .stat-delta {
121
+ :is(.mkt, .atelic-ui) .stat-delta {
110
122
  display: flex;
111
123
  flex-wrap: wrap;
112
124
  align-items: baseline;
@@ -117,27 +129,135 @@
117
129
  color: var(--neutral-500);
118
130
  font-variant-numeric: tabular-nums;
119
131
  }
120
- .mkt .stat-delta-change {
132
+ :is(.mkt, .atelic-ui) .stat-delta-change {
121
133
  display: inline-flex;
122
134
  align-items: center;
123
135
  gap: 2px;
124
136
  font-weight: 700;
125
137
  color: var(--tone);
126
138
  }
127
- .mkt .stat-delta-change svg {
139
+ :is(.mkt, .atelic-ui) .stat-delta-change svg {
128
140
  align-self: center;
129
141
  }
130
- .mkt .stat-delta-basis {
142
+ :is(.mkt, .atelic-ui) .stat-delta-basis {
131
143
  text-decoration: none;
132
144
  }
133
- .mkt .stat .note {
145
+ :is(.mkt, .atelic-ui) .stat .note {
134
146
  font-size: 0.8rem;
135
147
  color: var(--neutral-500);
136
148
  margin-top: 4px;
137
149
  }
138
150
 
151
+ :is(.mkt, .atelic-ui) .stat-unit {
152
+ margin-left: 0.2em;
153
+ font-size: 0.4em;
154
+ font-weight: 500;
155
+ letter-spacing: 0;
156
+ color: var(--neutral-500);
157
+ }
158
+
159
+ /* ---- StatRow, quiet: a glance row for a working page. A light figure in
160
+ the text color over a small capitalized label, the stats divided by a
161
+ hairline. `--stat-size` sets the figure. ---- */
162
+ :is(.mkt, .atelic-ui) .stat-row.is-quiet {
163
+ display: flex;
164
+ gap: 0;
165
+ }
166
+ :is(.mkt, .atelic-ui) .stat-row.is-quiet .stat {
167
+ flex: 1;
168
+ min-width: 64px;
169
+ padding-right: 16px;
170
+ }
171
+ :is(.mkt, .atelic-ui) .stat-row.is-quiet .stat + .stat {
172
+ padding-left: 16px;
173
+ border-left: 1px solid var(--neutral-200);
174
+ }
175
+ :is(.mkt, .atelic-ui) .stat-row.is-quiet .stat .num {
176
+ align-items: baseline;
177
+ gap: 4px;
178
+ font: 300 var(--stat-size, 28px) / 1 var(--font-sans);
179
+ /* After the shorthand, which would otherwise reset it. */
180
+ font-variant-numeric: tabular-nums;
181
+ letter-spacing: -0.02em;
182
+ color: var(--tone, var(--ink));
183
+ }
184
+ :is(.mkt, .atelic-ui) .stat-row.is-quiet .stat-unit {
185
+ margin-left: 0;
186
+ font: 500 12px / 1 var(--font-mono);
187
+ }
188
+ :is(.mkt, .atelic-ui) .stat-row.is-quiet .stat .lbl {
189
+ margin-top: 7px;
190
+ font: 500 10px / 1.2 var(--font-sans);
191
+ letter-spacing: 0.1em;
192
+ text-transform: uppercase;
193
+ color: var(--neutral-500);
194
+ }
195
+
196
+ /* ---- Disclosure: a section the page opens and closes. The toggle's
197
+ ::after covers the whole header row, so the meta and the chevron fold
198
+ it too while staying out of its name. ---- */
199
+ :is(.mkt, .atelic-ui) .disclosure {
200
+ border: 1px solid var(--neutral-200);
201
+ border-radius: 16px;
202
+ background: var(--ui-card, var(--card));
203
+ overflow: hidden;
204
+ }
205
+ :is(.mkt, .atelic-ui) .disclosure-head {
206
+ position: relative;
207
+ display: flex;
208
+ align-items: center;
209
+ gap: 10px;
210
+ padding: 14px 18px;
211
+ }
212
+ :is(.mkt, .atelic-ui) .disclosure-title {
213
+ display: flex;
214
+ min-width: 0;
215
+ margin: 0;
216
+ font: inherit;
217
+ }
218
+ :is(.mkt, .atelic-ui) .disclosure-toggle {
219
+ border: none;
220
+ background: transparent;
221
+ padding: 0;
222
+ text-align: left;
223
+ font: 500 17px / 1 var(--font-sans);
224
+ letter-spacing: -0.01em;
225
+ color: var(--ink);
226
+ cursor: pointer;
227
+ }
228
+ :is(.mkt, .atelic-ui) .disclosure-toggle::after {
229
+ content: "";
230
+ position: absolute;
231
+ inset: 0;
232
+ }
233
+ :is(.mkt, .atelic-ui) .disclosure-toggle:focus-visible {
234
+ outline: none;
235
+ }
236
+ :is(.mkt, .atelic-ui) .disclosure-toggle:focus-visible::after {
237
+ outline: 2px solid var(--disclosure-focus, var(--ink));
238
+ outline-offset: -2px;
239
+ border-radius: 15px;
240
+ }
241
+ :is(.mkt, .atelic-ui) .disclosure-meta {
242
+ display: inline-flex;
243
+ align-items: center;
244
+ color: var(--neutral-500);
245
+ }
246
+ :is(.mkt, .atelic-ui) .disclosure-chevron {
247
+ margin-left: auto;
248
+ flex-shrink: 0;
249
+ color: var(--neutral-500);
250
+ transition: transform 200ms ease;
251
+ }
252
+ :is(.mkt, .atelic-ui) .disclosure.is-open .disclosure-chevron {
253
+ transform: rotate(180deg);
254
+ }
255
+ :is(.mkt, .atelic-ui) .disclosure-body {
256
+ border-top: 1px solid var(--neutral-200);
257
+ }
258
+
139
259
  /* ---- Collapsible: a labeled line that opens to the detail behind it ---- */
140
- .mkt .collapsible > summary {
260
+ :is(.mkt, .atelic-ui) .collapsible > summary {
141
261
  display: flex;
142
262
  align-items: center;
143
263
  justify-content: space-between;
@@ -145,77 +265,77 @@
145
265
  cursor: pointer;
146
266
  list-style: none;
147
267
  }
148
- .mkt .collapsible > summary::-webkit-details-marker {
268
+ :is(.mkt, .atelic-ui) .collapsible > summary::-webkit-details-marker {
149
269
  display: none;
150
270
  }
151
- .mkt .collapsible-text {
271
+ :is(.mkt, .atelic-ui) .collapsible-text {
152
272
  display: flex;
153
273
  flex-direction: column;
154
274
  gap: 2px;
155
275
  min-width: 0;
156
276
  }
157
- .mkt .collapsible-label {
277
+ :is(.mkt, .atelic-ui) .collapsible-label {
158
278
  font-weight: 650;
159
279
  color: var(--ink);
160
280
  }
161
- .mkt .collapsible-summary {
281
+ :is(.mkt, .atelic-ui) .collapsible-summary {
162
282
  color: var(--ink-soft);
163
283
  text-wrap: pretty;
164
284
  }
165
- .mkt .collapsible > summary:hover .collapsible-label {
166
- color: var(--primary);
285
+ :is(.mkt, .atelic-ui) .collapsible > summary:hover .collapsible-label {
286
+ color: var(--ui-primary, var(--primary));
167
287
  }
168
- .mkt .collapsible-chevron {
288
+ :is(.mkt, .atelic-ui) .collapsible-chevron {
169
289
  flex: none;
170
290
  color: var(--neutral-500);
171
291
  transition: transform 0.15s;
172
292
  }
173
- .mkt .collapsible[open] > summary .collapsible-chevron {
293
+ :is(.mkt, .atelic-ui) .collapsible[open] > summary .collapsible-chevron {
174
294
  transform: rotate(180deg);
175
295
  }
176
- .mkt .collapsible-body {
296
+ :is(.mkt, .atelic-ui) .collapsible-body {
177
297
  margin-top: 12px;
178
298
  }
179
299
  @media (prefers-reduced-motion: reduce) {
180
- .mkt .collapsible-chevron {
300
+ :is(.mkt, .atelic-ui) .collapsible-chevron {
181
301
  transition: none;
182
302
  }
183
303
  }
184
304
  /* Paper cannot be clicked, so a printed collapsible is open. */
185
305
  @media print {
186
- .mkt .collapsible::details-content {
306
+ :is(.mkt, .atelic-ui) .collapsible::details-content {
187
307
  content-visibility: visible;
188
308
  display: block;
189
309
  }
190
- .mkt .collapsible-chevron {
310
+ :is(.mkt, .atelic-ui) .collapsible-chevron {
191
311
  display: none;
192
312
  }
193
313
  }
194
314
 
195
315
  /* ---- Callout: an icon in its tone beside the title, the explainer
196
316
  under it; boxed, it sits apart on a tinted ground ---- */
197
- .mkt .callout {
317
+ :is(.mkt, .atelic-ui) .callout {
198
318
  display: grid;
199
319
  grid-template-columns: auto minmax(0, 1fr);
200
320
  gap: 10px;
201
321
  align-items: start;
202
322
  }
203
- .mkt .callout-icon {
323
+ :is(.mkt, .atelic-ui) .callout-icon {
204
324
  margin-top: 0.15em;
205
325
  color: var(--tone);
206
326
  flex: none;
207
327
  }
208
- .mkt .callout-title {
328
+ :is(.mkt, .atelic-ui) .callout-title {
209
329
  font-weight: 650;
210
330
  color: var(--ink);
211
331
  text-wrap: pretty;
212
332
  }
213
- .mkt .callout-body {
333
+ :is(.mkt, .atelic-ui) .callout-body {
214
334
  margin-top: 2px;
215
335
  color: var(--ink-soft);
216
336
  text-wrap: pretty;
217
337
  }
218
- .mkt .callout.is-box {
338
+ :is(.mkt, .atelic-ui) .callout.is-box {
219
339
  padding: 14px 16px;
220
340
  border-radius: var(--radius-sm);
221
341
  background: var(--tone-bg);
@@ -223,7 +343,7 @@
223
343
  }
224
344
 
225
345
  /* ---- Checklist: checks passed or failed, two columns when there is room ---- */
226
- .mkt .checklist {
346
+ :is(.mkt, .atelic-ui) .checklist {
227
347
  list-style: none;
228
348
  margin: 0;
229
349
  padding: 0;
@@ -232,36 +352,36 @@
232
352
  gap: 12px 28px;
233
353
  }
234
354
  @media print, (min-width: 721px) {
235
- .mkt .checklist {
355
+ :is(.mkt, .atelic-ui) .checklist {
236
356
  grid-template-columns: repeat(2, minmax(0, 1fr));
237
357
  }
238
358
  }
239
- .mkt .checklist li {
359
+ :is(.mkt, .atelic-ui) .checklist li {
240
360
  display: grid;
241
361
  grid-template-columns: auto minmax(0, 1fr);
242
362
  gap: 10px;
243
363
  align-items: start;
244
364
  line-height: 1.4;
245
365
  }
246
- .mkt .checklist li > svg {
366
+ :is(.mkt, .atelic-ui) .checklist li > svg {
247
367
  margin-top: 0.1em;
248
368
  color: var(--tone);
249
369
  }
250
- .mkt .checklist-label {
370
+ :is(.mkt, .atelic-ui) .checklist-label {
251
371
  display: block;
252
372
  font-weight: 600;
253
373
  color: var(--ink);
254
374
  }
255
- .mkt .checklist li[data-tone="bad"] .checklist-label {
375
+ :is(.mkt, .atelic-ui) .checklist li[data-tone="bad"] .checklist-label {
256
376
  color: var(--tone);
257
377
  }
258
- .mkt .checklist-detail {
378
+ :is(.mkt, .atelic-ui) .checklist-detail {
259
379
  display: block;
260
380
  color: var(--ink-soft);
261
381
  }
262
382
 
263
383
  /* ---- The charts' shared key: a swatch, a figure, a label ---- */
264
- .mkt .chart-key {
384
+ :is(.mkt, .atelic-ui) .chart-key {
265
385
  list-style: none;
266
386
  margin: 10px 0 0;
267
387
  padding: 0;
@@ -271,17 +391,17 @@
271
391
  font-size: 0.82rem;
272
392
  color: var(--ink-soft);
273
393
  }
274
- .mkt .chart-key li {
394
+ :is(.mkt, .atelic-ui) .chart-key li {
275
395
  display: inline-flex;
276
396
  align-items: baseline;
277
397
  gap: 7px;
278
398
  }
279
- .mkt .chart-key b {
399
+ :is(.mkt, .atelic-ui) .chart-key b {
280
400
  font-weight: 650;
281
401
  color: var(--ink);
282
402
  font-variant-numeric: tabular-nums;
283
403
  }
284
- .mkt .chart-swatch {
404
+ :is(.mkt, .atelic-ui) .chart-swatch {
285
405
  width: 10px;
286
406
  height: 10px;
287
407
  border-radius: 3px;
@@ -291,35 +411,35 @@
291
411
  }
292
412
 
293
413
  /* ---- StackedBar: one bar, the parts of a whole ---- */
294
- .mkt .stacked-bar-track {
414
+ :is(.mkt, .atelic-ui) .stacked-bar-track {
295
415
  display: flex;
296
416
  gap: 2px;
297
417
  height: 16px;
298
418
  border-radius: 999px;
299
419
  overflow: hidden;
300
420
  }
301
- .mkt .stacked-bar-track span {
421
+ :is(.mkt, .atelic-ui) .stacked-bar-track span {
302
422
  background: var(--tone-fill);
303
423
  min-width: 4px;
304
424
  }
305
425
 
306
426
  /* ---- ColumnChart: a bar a period, the periods in labeled groups ---- */
307
- .mkt .column-chart-plot {
427
+ :is(.mkt, .atelic-ui) .column-chart-plot {
308
428
  display: flex;
309
429
  align-items: flex-end;
310
430
  gap: 12px;
311
431
  min-width: 0;
312
432
  }
313
- .mkt .column-chart-group {
433
+ :is(.mkt, .atelic-ui) .column-chart-group {
314
434
  flex: 1 1 0;
315
435
  min-width: 0;
316
436
  }
317
- .mkt .column-chart-bars {
437
+ :is(.mkt, .atelic-ui) .column-chart-bars {
318
438
  display: flex;
319
439
  align-items: flex-end;
320
440
  gap: 3px;
321
441
  }
322
- .mkt .column-chart-col {
442
+ :is(.mkt, .atelic-ui) .column-chart-col {
323
443
  flex: 1 1 0;
324
444
  min-width: 0;
325
445
  display: flex;
@@ -327,24 +447,24 @@
327
447
  align-items: center;
328
448
  gap: 3px;
329
449
  }
330
- .mkt .column-chart-value {
450
+ :is(.mkt, .atelic-ui) .column-chart-value {
331
451
  font-size: 0.64rem;
332
452
  font-variant-numeric: tabular-nums;
333
453
  color: var(--ink-soft);
334
454
  white-space: nowrap;
335
455
  }
336
- .mkt .column-chart-bar {
456
+ :is(.mkt, .atelic-ui) .column-chart-bar {
337
457
  width: 100%;
338
458
  max-width: 48px;
339
459
  border-radius: 4px 4px 0 0;
340
460
  background: var(--tone-fill);
341
461
  }
342
- .mkt .column-chart-label {
462
+ :is(.mkt, .atelic-ui) .column-chart-label {
343
463
  font-size: 0.66rem;
344
464
  color: var(--ink-soft);
345
465
  white-space: nowrap;
346
466
  }
347
- .mkt .column-chart-group-label {
467
+ :is(.mkt, .atelic-ui) .column-chart-group-label {
348
468
  display: flex;
349
469
  flex-wrap: wrap;
350
470
  justify-content: center;
@@ -358,7 +478,7 @@
358
478
  color: var(--neutral-500);
359
479
  white-space: nowrap;
360
480
  }
361
- .mkt .column-chart-group-label b {
481
+ :is(.mkt, .atelic-ui) .column-chart-group-label b {
362
482
  font-family: var(--font-sans);
363
483
  font-size: 0.8rem;
364
484
  letter-spacing: 0;
@@ -368,37 +488,37 @@
368
488
  }
369
489
 
370
490
  /* ---- ThresholdScale: zones, and a pin a reading ---- */
371
- .mkt .threshold-scale {
491
+ :is(.mkt, .atelic-ui) .threshold-scale {
372
492
  display: flex;
373
493
  flex-direction: column;
374
494
  gap: 10px;
375
495
  }
376
- .mkt .threshold-scale-row {
496
+ :is(.mkt, .atelic-ui) .threshold-scale-row {
377
497
  display: grid;
378
498
  grid-template-columns: 4.4em minmax(0, 1fr) 3.4em;
379
499
  gap: 10px;
380
500
  align-items: center;
381
501
  }
382
- .mkt .threshold-scale-label {
502
+ :is(.mkt, .atelic-ui) .threshold-scale-label {
383
503
  color: var(--ink-soft);
384
504
  }
385
- .mkt .threshold-scale-track {
505
+ :is(.mkt, .atelic-ui) .threshold-scale-track {
386
506
  position: relative;
387
507
  display: flex;
388
508
  gap: 2px;
389
509
  height: 12px;
390
510
  }
391
- .mkt .threshold-scale-zone {
511
+ :is(.mkt, .atelic-ui) .threshold-scale-zone {
392
512
  background: var(--tone-fill);
393
513
  opacity: 0.32;
394
514
  }
395
- .mkt .threshold-scale-zone:first-child {
515
+ :is(.mkt, .atelic-ui) .threshold-scale-zone:first-child {
396
516
  border-radius: 999px 0 0 999px;
397
517
  }
398
- .mkt .threshold-scale-zone:nth-last-child(2) {
518
+ :is(.mkt, .atelic-ui) .threshold-scale-zone:nth-last-child(2) {
399
519
  border-radius: 0 999px 999px 0;
400
520
  }
401
- .mkt .threshold-scale-pin {
521
+ :is(.mkt, .atelic-ui) .threshold-scale-pin {
402
522
  position: absolute;
403
523
  top: 50%;
404
524
  width: 18px;
@@ -406,30 +526,30 @@
406
526
  margin: -9px 0 0 -9px;
407
527
  border-radius: 50%;
408
528
  background: var(--tone-fill);
409
- border: 3px solid var(--card);
529
+ border: 3px solid var(--ui-card, var(--card));
410
530
  box-shadow: 0 0 0 1.5px var(--tone-fill);
411
531
  }
412
- .mkt .threshold-scale-value {
532
+ :is(.mkt, .atelic-ui) .threshold-scale-value {
413
533
  font-weight: 700;
414
534
  font-variant-numeric: tabular-nums;
415
535
  color: var(--tone);
416
536
  text-align: right;
417
537
  }
418
- .mkt .threshold-scale-axis {
538
+ :is(.mkt, .atelic-ui) .threshold-scale-axis {
419
539
  position: relative;
420
540
  display: flex;
421
541
  gap: 2px;
422
542
  height: 2.3em;
423
543
  font-size: 0.8rem;
424
544
  }
425
- .mkt .threshold-scale-axis > span {
545
+ :is(.mkt, .atelic-ui) .threshold-scale-axis > span {
426
546
  color: var(--tone);
427
547
  font-weight: 600;
428
548
  white-space: nowrap;
429
549
  overflow: hidden;
430
550
  text-overflow: ellipsis;
431
551
  }
432
- .mkt .threshold-scale-axis > i {
552
+ :is(.mkt, .atelic-ui) .threshold-scale-axis > i {
433
553
  position: absolute;
434
554
  top: 1.25em;
435
555
  transform: translateX(-50%);
@@ -443,12 +563,12 @@
443
563
  /* ---- DataTable: on a phone each row stacks, its facts wearing their
444
564
  column's label, and a dropdown sorts; wide, it is a table whose
445
565
  headings sort ---- */
446
- .mkt .data-table table,
447
- .mkt .data-table tbody {
566
+ :is(.mkt, .atelic-ui) .data-table table,
567
+ :is(.mkt, .atelic-ui) .data-table tbody {
448
568
  display: block;
449
569
  width: 100%;
450
570
  }
451
- .mkt .data-table thead {
571
+ :is(.mkt, .atelic-ui) .data-table thead {
452
572
  position: absolute;
453
573
  width: 1px;
454
574
  height: 1px;
@@ -456,7 +576,7 @@
456
576
  clip: rect(0, 0, 0, 0);
457
577
  white-space: nowrap;
458
578
  }
459
- .mkt .data-table-sort {
579
+ :is(.mkt, .atelic-ui) .data-table-sort {
460
580
  display: flex;
461
581
  align-items: center;
462
582
  gap: 8px;
@@ -464,16 +584,16 @@
464
584
  font-size: 0.85rem;
465
585
  color: var(--ink-soft);
466
586
  }
467
- .mkt .data-table-sort select {
587
+ :is(.mkt, .atelic-ui) .data-table-sort select {
468
588
  font: inherit;
469
589
  color: var(--ink);
470
- background: var(--card);
590
+ background: var(--ui-card, var(--card));
471
591
  border: 1px solid var(--neutral-200);
472
592
  border-radius: var(--radius-sm);
473
593
  padding: 5px 8px;
474
594
  max-width: 100%;
475
595
  }
476
- .mkt .data-table tbody tr {
596
+ :is(.mkt, .atelic-ui) .data-table tbody tr {
477
597
  display: flex;
478
598
  flex-wrap: wrap;
479
599
  align-items: center;
@@ -482,25 +602,25 @@
482
602
  border-bottom: 1px solid var(--neutral-100);
483
603
  line-height: 1.35;
484
604
  }
485
- .mkt .data-table tbody tr:first-child {
605
+ :is(.mkt, .atelic-ui) .data-table tbody tr:first-child {
486
606
  border-top: 1.5px solid var(--ink);
487
607
  }
488
- .mkt .data-table tbody tr:last-child {
608
+ :is(.mkt, .atelic-ui) .data-table tbody tr:last-child {
489
609
  border-bottom: 0;
490
610
  padding-bottom: 0;
491
611
  }
492
- .mkt .data-table tbody th {
612
+ :is(.mkt, .atelic-ui) .data-table tbody th {
493
613
  width: 100%;
494
614
  text-align: left;
495
615
  font-weight: 400;
496
616
  }
497
- .mkt .data-table td {
617
+ :is(.mkt, .atelic-ui) .data-table td {
498
618
  display: inline-flex;
499
619
  align-items: center;
500
620
  gap: 6px;
501
621
  font-variant-numeric: tabular-nums;
502
622
  }
503
- .mkt .data-table td::before {
623
+ :is(.mkt, .atelic-ui) .data-table td::before {
504
624
  content: attr(data-label);
505
625
  font-family: var(--font-mono);
506
626
  font-size: 0.7rem;
@@ -509,33 +629,33 @@
509
629
  color: var(--neutral-500);
510
630
  }
511
631
  @media print, (min-width: 721px) {
512
- .mkt .data-table-sort {
632
+ :is(.mkt, .atelic-ui) .data-table-sort {
513
633
  display: none;
514
634
  }
515
- .mkt .data-table table {
635
+ :is(.mkt, .atelic-ui) .data-table table {
516
636
  display: table;
517
637
  border-collapse: collapse;
518
638
  }
519
- .mkt .data-table tbody {
639
+ :is(.mkt, .atelic-ui) .data-table tbody {
520
640
  display: table-row-group;
521
641
  }
522
- .mkt .data-table thead {
642
+ :is(.mkt, .atelic-ui) .data-table thead {
523
643
  position: static;
524
644
  width: auto;
525
645
  height: auto;
526
646
  overflow: visible;
527
647
  clip: auto;
528
648
  }
529
- .mkt .data-table tbody tr {
649
+ :is(.mkt, .atelic-ui) .data-table tbody tr {
530
650
  display: table-row;
531
651
  padding: 0;
532
652
  border: 0;
533
653
  }
534
- .mkt .data-table tbody tr:first-child {
654
+ :is(.mkt, .atelic-ui) .data-table tbody tr:first-child {
535
655
  border-top: 0;
536
656
  }
537
- .mkt .data-table th,
538
- .mkt .data-table td {
657
+ :is(.mkt, .atelic-ui) .data-table th,
658
+ :is(.mkt, .atelic-ui) .data-table td {
539
659
  display: table-cell;
540
660
  padding: 7px 14px 7px 0;
541
661
  border-bottom: 1px solid var(--neutral-100);
@@ -543,23 +663,23 @@
543
663
  vertical-align: middle;
544
664
  font-weight: 400;
545
665
  }
546
- .mkt .data-table tbody th {
666
+ :is(.mkt, .atelic-ui) .data-table tbody th {
547
667
  width: auto;
548
668
  }
549
- .mkt .data-table td::before {
669
+ :is(.mkt, .atelic-ui) .data-table td::before {
550
670
  content: none;
551
671
  }
552
- .mkt .data-table th:last-child,
553
- .mkt .data-table td:last-child {
672
+ :is(.mkt, .atelic-ui) .data-table th:last-child,
673
+ :is(.mkt, .atelic-ui) .data-table td:last-child {
554
674
  padding-right: 0;
555
675
  }
556
- .mkt .data-table [data-align="center"] {
676
+ :is(.mkt, .atelic-ui) .data-table [data-align="center"] {
557
677
  text-align: center;
558
678
  }
559
- .mkt .data-table [data-align="right"] {
679
+ :is(.mkt, .atelic-ui) .data-table [data-align="right"] {
560
680
  text-align: right;
561
681
  }
562
- .mkt .data-table thead th {
682
+ :is(.mkt, .atelic-ui) .data-table thead th {
563
683
  padding-top: 0;
564
684
  padding-bottom: 8px;
565
685
  border-bottom: 1.5px solid var(--ink);
@@ -571,7 +691,7 @@
571
691
  white-space: nowrap;
572
692
  vertical-align: bottom;
573
693
  }
574
- .mkt .data-table thead th button {
694
+ :is(.mkt, .atelic-ui) .data-table thead th button {
575
695
  display: inline-flex;
576
696
  align-items: center;
577
697
  gap: 4px;
@@ -584,23 +704,23 @@
584
704
  color: inherit;
585
705
  cursor: pointer;
586
706
  }
587
- .mkt .data-table thead th button:hover,
588
- .mkt .data-table thead th button[data-active] {
589
- color: var(--primary);
707
+ :is(.mkt, .atelic-ui) .data-table thead th button:hover,
708
+ :is(.mkt, .atelic-ui) .data-table thead th button[data-active] {
709
+ color: var(--ui-primary, var(--primary));
590
710
  }
591
- .mkt .data-table thead th button svg {
711
+ :is(.mkt, .atelic-ui) .data-table thead th button svg {
592
712
  opacity: 0.55;
593
713
  }
594
- .mkt .data-table thead th button[data-active] svg {
714
+ :is(.mkt, .atelic-ui) .data-table thead th button[data-active] svg {
595
715
  opacity: 1;
596
716
  }
597
- .mkt .data-table tbody tr:last-child th,
598
- .mkt .data-table tbody tr:last-child td {
717
+ :is(.mkt, .atelic-ui) .data-table tbody tr:last-child th,
718
+ :is(.mkt, .atelic-ui) .data-table tbody tr:last-child td {
599
719
  border-bottom: 0;
600
720
  }
601
721
  }
602
722
  @media print {
603
- .mkt .data-table thead th button svg {
723
+ :is(.mkt, .atelic-ui) .data-table thead th button svg {
604
724
  display: none;
605
725
  }
606
726
  }