@atelic-action/ui 2.1.0 → 2.2.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 +5 -1
- package/package.json +1 -1
- package/src/artifact/GradeChip.tsx +2 -2
- package/src/components/Rank.tsx +37 -0
- package/src/components/ReportCard.tsx +153 -0
- package/src/components/index.ts +9 -0
- package/src/styles/artifact.css +7 -50
- package/src/styles/components.css +287 -0
package/README.md
CHANGED
|
@@ -22,6 +22,8 @@ Every export, grouped by the entry it is imported from. The gallery column links
|
|
|
22
22
|
| `Checklist` | `@atelic-action/ui/components` | A short list of checks, each passed or failed | [#checklist](https://ui.atelic.me/#checklist) |
|
|
23
23
|
| `IconList` | `@atelic-action/ui/components` | A list whose bullets are icons, one for the list or one per item, each in a tone | [#icon-list](https://ui.atelic.me/#icon-list) |
|
|
24
24
|
| `DataTable` | `@atelic-action/ui/components` | A table whose first column names each row, sortable, stacking on a phone | [#data-table](https://ui.atelic.me/#data-table) |
|
|
25
|
+
| `ReportCard` | `@atelic-action/ui/components` | A name, a grade, and a line per row, in labeled groups, each row a link to its section | [#report-card](https://ui.atelic.me/#report-card) |
|
|
26
|
+
| `Rank` | `@atelic-action/ui/components` | A figure with its badge in a tone, for a table cell: a rank, a place in an answer, or a dash | [#rank](https://ui.atelic.me/#rank) |
|
|
25
27
|
| `StatRow` | `@atelic-action/ui/components` | A row of large figures with periods, deltas, and trends | [#stat-row](https://ui.atelic.me/#stat-row) |
|
|
26
28
|
| `Callout` | `@atelic-action/ui/components` | One finding: an icon in its tone, the title, the explainer | [#callout](https://ui.atelic.me/#callout) |
|
|
27
29
|
| `Disclosure` | `@atelic-action/ui/components` | A section that folds: controlled, an app's card; uncontrolled, a native details fold that prints open | [#disclosure](https://ui.atelic.me/#disclosure) |
|
|
@@ -353,7 +355,7 @@ export function ArtifactShell(props: Omit<ArtifactShellProps, "currentPath">) {
|
|
|
353
355
|
}
|
|
354
356
|
```
|
|
355
357
|
|
|
356
|
-
The pieces import no stylesheet of their own. A site imports `@atelic-action/ui/styles/artifact.css` in its base sheet after `gate.css`, then `@atelic-action/artifact/styles/documents.css`, and then its `theme.css`, so every page carries the rules whichever route it is. This sheet holds the pieces alone (the
|
|
358
|
+
The pieces import no stylesheet of their own. A site imports `@atelic-action/ui/styles/artifact.css` in its base sheet after `gate.css`, then `@atelic-action/artifact/styles/documents.css`, and then its `theme.css`, so every page carries the rules whichever route it is. This sheet holds the pieces alone (the sign off, the shot, and the shell's logo rule; the grade chip's rules sit in `components.css`, since `ReportCard` wears it) and sets no `@page`; both `@page` rules, the monthly page's and then the writeup's, moved with the documents into their sheet, where their order stays load bearing.
|
|
357
359
|
|
|
358
360
|
### The Artifact Vite Config
|
|
359
361
|
|
|
@@ -696,6 +698,8 @@ The blocks a page composes, each styled in `styles/components.css` inside the `a
|
|
|
696
698
|
|---|---|---|
|
|
697
699
|
| `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 |
|
|
698
700
|
| `IconList` | A list whose bullets are icons | `icon` and `tone` on the list set every bullet; an item's own override them. The bullet is decoration, so a screen reader hears only the items. A dot when no icon is set; the text's color when no tone is |
|
|
701
|
+
| `ReportCard` | A name, a grade, and one line on it per row, in labeled groups, each row's parts centered on it | A row with `href` is a link from edge to edge with a chevron at its end; a link inside `line` still goes where it points. `before` sets the earlier grade faded beside the grade; no `grade` draws a dash read as not graded. `legend` explains the letters beside the `title`, or under the card with `legendAt="foot"`; `labels` sets the screen reader's words. The grade is `GradeChip` |
|
|
702
|
+
| `Rank` | A figure with its badge, for a table cell | `figure` is the rank, the place, or a dash for none; `tone` colors the badge and `meaning` names it for a screen reader; `href` links the figure, in a new tab with `newTab`. The figure's slot is a fixed width, so the badges line up down a column |
|
|
699
703
|
| `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. Every other row sits on a tinted ground; `striped={false}` turns that off. A column marked `fit` hugs its content, for a swatch or a mark beside a figure |
|
|
700
704
|
| `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 |
|
|
701
705
|
| `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 |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atelic-action/ui",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Shared UI for the Atelic templates: site chrome, base styles, the component library, the access gate, and the app shell they install",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -18,8 +18,8 @@ export const gradeLabel: Record<WriteupGrade, string> = {
|
|
|
18
18
|
/**
|
|
19
19
|
* The letter as a chip in its grade's color. The documents in
|
|
20
20
|
* @atelic-action/artifact (the writeup, the read, the monthly page) wear it,
|
|
21
|
-
* so its styles live in
|
|
22
|
-
* a document's stylesheet.
|
|
21
|
+
* and so does ReportCard, so its styles live in styles/components.css, which
|
|
22
|
+
* an app may load alone; never in a document's stylesheet.
|
|
23
23
|
*/
|
|
24
24
|
export function GradeChip({ grade }: { grade: WriteupGrade }) {
|
|
25
25
|
return (
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { newTabProps } from "../lib/newTabProps";
|
|
2
|
+
import type { Tone } from "./tone";
|
|
3
|
+
|
|
4
|
+
export interface RankProps {
|
|
5
|
+
/** The figure: a rank, a place in an answer, or a dash for none. */
|
|
6
|
+
figure: string;
|
|
7
|
+
/** The tone its badge reads in. */
|
|
8
|
+
tone: Tone;
|
|
9
|
+
/** What the badge means, for a screen reader, e.g. "Top ten". */
|
|
10
|
+
meaning: string;
|
|
11
|
+
/** Where the figure leads, e.g. the page that holds the rank. */
|
|
12
|
+
href?: string;
|
|
13
|
+
/** Open the link in a new tab. */
|
|
14
|
+
newTab?: boolean;
|
|
15
|
+
className?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A figure with its badge, for a table cell: the badge in its tone on the
|
|
20
|
+
* left, the figure on the right, linked when it has somewhere to go. The
|
|
21
|
+
* figure's slot is a fixed width, so every rank in a column is the same width
|
|
22
|
+
* and the badges line up down it.
|
|
23
|
+
*/
|
|
24
|
+
export function Rank({ figure, tone, meaning, href, newTab, className }: RankProps) {
|
|
25
|
+
return (
|
|
26
|
+
<span className={["rank", className].filter(Boolean).join(" ")} data-tone={tone}>
|
|
27
|
+
<span className="chart-swatch" role="img" aria-label={meaning} />
|
|
28
|
+
{href ? (
|
|
29
|
+
<a href={href} {...newTabProps(href, newTab)}>
|
|
30
|
+
{figure}
|
|
31
|
+
</a>
|
|
32
|
+
) : (
|
|
33
|
+
<span>{figure}</span>
|
|
34
|
+
)}
|
|
35
|
+
</span>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { ChevronRight } from "lucide-react";
|
|
2
|
+
import { Fragment, type ReactNode, useId } from "react";
|
|
3
|
+
import { GradeChip, gradeLabel, type WriteupGrade } from "../artifact/GradeChip";
|
|
4
|
+
|
|
5
|
+
export interface ReportCardRow {
|
|
6
|
+
/** The row's name, e.g. a section's trade name. */
|
|
7
|
+
name: ReactNode;
|
|
8
|
+
/** Where the row leads, e.g. "#technical-seo"; the whole row is the link when set. */
|
|
9
|
+
href?: string;
|
|
10
|
+
/** The grade; a dash, read as not graded, when left out. */
|
|
11
|
+
grade?: WriteupGrade;
|
|
12
|
+
/** The earlier grade, faded beside the grade: what moved. */
|
|
13
|
+
before?: WriteupGrade;
|
|
14
|
+
/** The line behind the grade, in the owner's words. */
|
|
15
|
+
line?: ReactNode;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ReportCardGroup {
|
|
19
|
+
/** The label over the group's rows, e.g. "Health". */
|
|
20
|
+
title: string;
|
|
21
|
+
rows: ReportCardRow[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** The words a screen reader hears around the letters. */
|
|
25
|
+
export interface ReportCardLabels {
|
|
26
|
+
/** Before a grade; "Grade". */
|
|
27
|
+
grade: string;
|
|
28
|
+
/** Before the earlier grade; "Before". */
|
|
29
|
+
before: string;
|
|
30
|
+
/** For a row with no grade; "Not graded yet". */
|
|
31
|
+
notGraded: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const reportCardLabels: ReportCardLabels = {
|
|
35
|
+
grade: "Grade",
|
|
36
|
+
before: "Before",
|
|
37
|
+
notGraded: "Not graded yet",
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export interface ReportCardProps {
|
|
41
|
+
id?: string;
|
|
42
|
+
/** The heading over the card, e.g. "At a Glance". */
|
|
43
|
+
title?: ReactNode;
|
|
44
|
+
/** What a screen reader calls the card when it has no title. */
|
|
45
|
+
label?: string;
|
|
46
|
+
/** What each letter means, in a few words each, right of the title. */
|
|
47
|
+
legend?: Array<{ grade: WriteupGrade; label: string }>;
|
|
48
|
+
/** Where the legend sits: beside the title, or under the card so nothing sits between a heading and the rows. */
|
|
49
|
+
legendAt?: "head" | "foot";
|
|
50
|
+
groups: ReportCardGroup[];
|
|
51
|
+
labels?: Partial<ReportCardLabels>;
|
|
52
|
+
className?: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* A report card: a name, a grade, and one line on it per row, in labeled
|
|
57
|
+
* groups, each row's parts centered on it. A group's label
|
|
58
|
+
* is not a heading: the card sits inside a document whose outline the
|
|
59
|
+
* sections already carry. A row with an
|
|
60
|
+
* href is a link from edge to edge, with a chevron at its end; a link inside
|
|
61
|
+
* the line still goes where it points. The grade is the chip every document
|
|
62
|
+
* wears, and the earlier grade, when there is one, sits faded beside it.
|
|
63
|
+
*/
|
|
64
|
+
export function ReportCard({
|
|
65
|
+
id,
|
|
66
|
+
title,
|
|
67
|
+
label,
|
|
68
|
+
legend,
|
|
69
|
+
legendAt = "head",
|
|
70
|
+
groups,
|
|
71
|
+
labels: overrides,
|
|
72
|
+
className,
|
|
73
|
+
}: ReportCardProps) {
|
|
74
|
+
// An override left undefined keeps the default, so a caller can pass what it has.
|
|
75
|
+
const titleId = useId();
|
|
76
|
+
const labels = { ...reportCardLabels };
|
|
77
|
+
for (const key of Object.keys(labels) as Array<keyof ReportCardLabels>) {
|
|
78
|
+
const word = overrides?.[key];
|
|
79
|
+
if (word !== undefined) labels[key] = word;
|
|
80
|
+
}
|
|
81
|
+
const shown = groups.filter((group) => group.rows.length > 0);
|
|
82
|
+
const hasLegend = legend !== undefined && legend.length > 0;
|
|
83
|
+
const key = hasLegend && (
|
|
84
|
+
<ul className={legendAt === "foot" ? "rc-legend rc-legend-foot" : "rc-legend"}>
|
|
85
|
+
{legend.map((entry, i) => (
|
|
86
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: a legend is static, and two entries may share a letter
|
|
87
|
+
<li key={`${i}-${entry.grade}`}>
|
|
88
|
+
<GradeChip grade={entry.grade} />
|
|
89
|
+
<span>{entry.label}</span>
|
|
90
|
+
</li>
|
|
91
|
+
))}
|
|
92
|
+
</ul>
|
|
93
|
+
);
|
|
94
|
+
return (
|
|
95
|
+
<section
|
|
96
|
+
className={["report-card", className].filter(Boolean).join(" ")}
|
|
97
|
+
id={id}
|
|
98
|
+
aria-labelledby={title ? titleId : undefined}
|
|
99
|
+
aria-label={title ? undefined : label}
|
|
100
|
+
>
|
|
101
|
+
{(title || (hasLegend && legendAt === "head")) && (
|
|
102
|
+
<div className="rc-head">
|
|
103
|
+
{title && (
|
|
104
|
+
<h2 className="rc-title" id={titleId}>
|
|
105
|
+
{title}
|
|
106
|
+
</h2>
|
|
107
|
+
)}
|
|
108
|
+
{legendAt === "head" && key}
|
|
109
|
+
</div>
|
|
110
|
+
)}
|
|
111
|
+
<div className="rc-card">
|
|
112
|
+
{shown.map((group, g) => (
|
|
113
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: two groups may share a title
|
|
114
|
+
<Fragment key={`${g}-${group.title}`}>
|
|
115
|
+
<div className="rc-group">{group.title}</div>
|
|
116
|
+
{group.rows.map((row, i) => (
|
|
117
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: rows are static content in a fixed order
|
|
118
|
+
<div className="rc-row" key={i}>
|
|
119
|
+
<span className="rc-grade">
|
|
120
|
+
{row.before && (
|
|
121
|
+
<span className="rc-before">
|
|
122
|
+
<span className="rc-sr">{`${labels.before}: ${gradeLabel[row.before]}. `}</span>
|
|
123
|
+
<span aria-hidden="true">
|
|
124
|
+
<GradeChip grade={row.before} />
|
|
125
|
+
</span>
|
|
126
|
+
</span>
|
|
127
|
+
)}
|
|
128
|
+
{row.grade ? (
|
|
129
|
+
<>
|
|
130
|
+
<span className="rc-sr">{`${labels.grade} ${gradeLabel[row.grade]}: `}</span>
|
|
131
|
+
<span aria-hidden="true">
|
|
132
|
+
<GradeChip grade={row.grade} />
|
|
133
|
+
</span>
|
|
134
|
+
</>
|
|
135
|
+
) : (
|
|
136
|
+
<>
|
|
137
|
+
<span className="rc-sr">{`${labels.notGraded}: `}</span>
|
|
138
|
+
<span className="rc-dash" aria-hidden="true" />
|
|
139
|
+
</>
|
|
140
|
+
)}
|
|
141
|
+
</span>
|
|
142
|
+
<b className="rc-name">{row.href ? <a href={row.href}>{row.name}</a> : row.name}</b>
|
|
143
|
+
{row.line != null && <span className="rc-line">{row.line}</span>}
|
|
144
|
+
{row.href && <ChevronRight className="rc-go" size={18} aria-hidden="true" />}
|
|
145
|
+
</div>
|
|
146
|
+
))}
|
|
147
|
+
</Fragment>
|
|
148
|
+
))}
|
|
149
|
+
</div>
|
|
150
|
+
{legendAt === "foot" && key}
|
|
151
|
+
</section>
|
|
152
|
+
);
|
|
153
|
+
}
|
package/src/components/index.ts
CHANGED
|
@@ -25,6 +25,15 @@ export {
|
|
|
25
25
|
export { GateLogin, type GateLoginProps } from "./GateLogin";
|
|
26
26
|
export { IconList, type IconListItem, type IconListProps } from "./IconList";
|
|
27
27
|
export { NotFound, type NotFoundProps } from "./NotFound";
|
|
28
|
+
export { Rank, type RankProps } from "./Rank";
|
|
29
|
+
export {
|
|
30
|
+
ReportCard,
|
|
31
|
+
type ReportCardGroup,
|
|
32
|
+
type ReportCardLabels,
|
|
33
|
+
type ReportCardProps,
|
|
34
|
+
type ReportCardRow,
|
|
35
|
+
reportCardLabels,
|
|
36
|
+
} from "./ReportCard";
|
|
28
37
|
export { StackedBar, type StackedBarProps, type StackedBarSegment } from "./StackedBar";
|
|
29
38
|
export { describeStarRating, StarRating, type StarRatingProps } from "./StarRating";
|
|
30
39
|
export {
|
package/src/styles/artifact.css
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* The pieces the artifact documents are built from, under src/artifact: the
|
|
2
|
-
|
|
2
|
+
sign off, the shot, and the shell's logo and print rules (the grade chip's
|
|
3
|
+
rules sit in components.css, since ReportCard wears it there). The
|
|
3
4
|
composed documents (the writeup, the read, the monthly page, the note,
|
|
4
5
|
and the sealed document stage) and their sheet, with both @page rules,
|
|
5
6
|
live in @atelic-action/artifact (its styles/documents.css), which a site
|
|
@@ -8,58 +9,17 @@
|
|
|
8
9
|
a layer still wins over it whatever its specificity. */
|
|
9
10
|
@layer atelic-ui {
|
|
10
11
|
/* =================================================================
|
|
11
|
-
SHARED: the vocabulary every document wears, the
|
|
12
|
-
(
|
|
12
|
+
SHARED: the vocabulary every document wears, the sign off lockup
|
|
13
|
+
(SignOff) and the shot (Shot). A
|
|
13
14
|
class used by more than one document lives in the base sheet chain
|
|
14
15
|
rather than in a component's stylesheet, so it can never vanish from
|
|
15
16
|
a page that does not import the component that defined it.
|
|
16
17
|
|
|
17
|
-
Tokens only
|
|
18
|
+
Tokens only.
|
|
18
19
|
================================================================= */
|
|
19
20
|
|
|
20
|
-
/*
|
|
21
|
-
|
|
22
|
-
display: inline-block;
|
|
23
|
-
vertical-align: 0.1em;
|
|
24
|
-
margin-left: 0.6em;
|
|
25
|
-
padding: 0.1em 0.55em 0.12em;
|
|
26
|
-
border: 1px solid var(--hairline-strong);
|
|
27
|
-
border-radius: 999px;
|
|
28
|
-
font-family: var(--font-mono);
|
|
29
|
-
font-size: 7pt;
|
|
30
|
-
font-weight: 600;
|
|
31
|
-
letter-spacing: 0.08em;
|
|
32
|
-
text-transform: uppercase;
|
|
33
|
-
color: var(--ink-soft);
|
|
34
|
-
line-height: 1.3;
|
|
35
|
-
min-width: 1.6em;
|
|
36
|
-
text-align: center;
|
|
37
|
-
}
|
|
38
|
-
/* The four grade colors are a deliberate literal set: a grade reads
|
|
39
|
-
green, yellow, orange, red
|
|
40
|
-
everywhere, whatever the brand's palette, so they are not theme tokens.
|
|
41
|
-
The orange sits between the yellow's hue and the red's so the four stay
|
|
42
|
-
apart on paper, and its letter clears AA on its own tint. */
|
|
43
|
-
.atelic-ui .op-grade[data-grade="a"] {
|
|
44
|
-
color: #1b7f3b;
|
|
45
|
-
border-color: #1b7f3b;
|
|
46
|
-
background: #eaf6ee;
|
|
47
|
-
}
|
|
48
|
-
.atelic-ui .op-grade[data-grade="b"] {
|
|
49
|
-
color: #9a6b00;
|
|
50
|
-
border-color: #c99a1a;
|
|
51
|
-
background: #fdf5dc;
|
|
52
|
-
}
|
|
53
|
-
.atelic-ui .op-grade[data-grade="c"] {
|
|
54
|
-
color: #a84500;
|
|
55
|
-
border-color: #e8650a;
|
|
56
|
-
background: #fff0e3;
|
|
57
|
-
}
|
|
58
|
-
.atelic-ui .op-grade[data-grade="d"] {
|
|
59
|
-
color: #b3261e;
|
|
60
|
-
border-color: #b3261e;
|
|
61
|
-
background: #fdecea;
|
|
62
|
-
}
|
|
21
|
+
/* The grade chip's rules, its phone size included, live in components.css,
|
|
22
|
+
since ReportCard wears it there and an app may load that sheet alone. */
|
|
63
23
|
|
|
64
24
|
/* ---- the sign off lockup: its container sets the size and the color ---- */
|
|
65
25
|
.atelic-ui .op-who {
|
|
@@ -93,9 +53,6 @@
|
|
|
93
53
|
}
|
|
94
54
|
|
|
95
55
|
@media screen and (max-width: 899px) {
|
|
96
|
-
.atelic-ui .op-grade {
|
|
97
|
-
font-size: 10px;
|
|
98
|
-
}
|
|
99
56
|
.atelic-ui .op-sign {
|
|
100
57
|
font-size: 22px;
|
|
101
58
|
}
|
|
@@ -417,6 +417,275 @@
|
|
|
417
417
|
color: var(--tone, currentColor);
|
|
418
418
|
}
|
|
419
419
|
|
|
420
|
+
/* ---- GradeChip: the letter in its grade's color. The four colors are a
|
|
421
|
+
deliberate literal set, green, yellow, orange, red, whatever the
|
|
422
|
+
brand's palette, so they are not theme tokens. It lives here rather
|
|
423
|
+
than in artifact.css because ReportCard wears it, and an app may load
|
|
424
|
+
this sheet alone. ---- */
|
|
425
|
+
.atelic-ui .op-grade {
|
|
426
|
+
display: inline-block;
|
|
427
|
+
vertical-align: 0.1em;
|
|
428
|
+
margin-left: 0.6em;
|
|
429
|
+
padding: 0.1em 0.55em 0.12em;
|
|
430
|
+
border: 1px solid var(--hairline-strong);
|
|
431
|
+
border-radius: 999px;
|
|
432
|
+
font-family: var(--font-mono);
|
|
433
|
+
font-size: 7pt;
|
|
434
|
+
font-weight: 600;
|
|
435
|
+
letter-spacing: 0.08em;
|
|
436
|
+
text-transform: uppercase;
|
|
437
|
+
color: var(--ink-soft);
|
|
438
|
+
line-height: 1.3;
|
|
439
|
+
min-width: 1.6em;
|
|
440
|
+
text-align: center;
|
|
441
|
+
}
|
|
442
|
+
/* The orange sits between the yellow's hue and the red's so the four
|
|
443
|
+
stay apart on paper, and its letter clears AA on its own tint. */
|
|
444
|
+
.atelic-ui .op-grade[data-grade="a"] {
|
|
445
|
+
color: #1b7f3b;
|
|
446
|
+
border-color: #1b7f3b;
|
|
447
|
+
background: #eaf6ee;
|
|
448
|
+
}
|
|
449
|
+
.atelic-ui .op-grade[data-grade="b"] {
|
|
450
|
+
color: #9a6b00;
|
|
451
|
+
border-color: #c99a1a;
|
|
452
|
+
background: #fdf5dc;
|
|
453
|
+
}
|
|
454
|
+
.atelic-ui .op-grade[data-grade="c"] {
|
|
455
|
+
color: #a84500;
|
|
456
|
+
border-color: #e8650a;
|
|
457
|
+
background: #fff0e3;
|
|
458
|
+
}
|
|
459
|
+
.atelic-ui .op-grade[data-grade="d"] {
|
|
460
|
+
color: #b3261e;
|
|
461
|
+
border-color: #b3261e;
|
|
462
|
+
background: #fdecea;
|
|
463
|
+
}
|
|
464
|
+
@media screen and (max-width: 899px) {
|
|
465
|
+
.atelic-ui .op-grade {
|
|
466
|
+
font-size: 10px;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/* ---- ReportCard: a name, a grade, and a line per row, in labeled
|
|
471
|
+
groups ---- */
|
|
472
|
+
.atelic-ui .report-card {
|
|
473
|
+
min-width: 0;
|
|
474
|
+
}
|
|
475
|
+
.atelic-ui .rc-head {
|
|
476
|
+
display: flex;
|
|
477
|
+
flex-wrap: wrap;
|
|
478
|
+
align-items: center;
|
|
479
|
+
justify-content: space-between;
|
|
480
|
+
gap: 8px 20px;
|
|
481
|
+
margin-bottom: 10px;
|
|
482
|
+
}
|
|
483
|
+
.atelic-ui .rc-title {
|
|
484
|
+
margin: 0;
|
|
485
|
+
font-family: var(--font-mono);
|
|
486
|
+
font-size: 13px;
|
|
487
|
+
font-weight: 500;
|
|
488
|
+
letter-spacing: 0.1em;
|
|
489
|
+
text-transform: uppercase;
|
|
490
|
+
color: var(--neutral-500);
|
|
491
|
+
}
|
|
492
|
+
.atelic-ui .rc-legend {
|
|
493
|
+
list-style: none;
|
|
494
|
+
margin: 0 0 0 auto;
|
|
495
|
+
padding: 0;
|
|
496
|
+
display: flex;
|
|
497
|
+
flex-wrap: wrap;
|
|
498
|
+
gap: 6px 16px;
|
|
499
|
+
font-size: 13.5px;
|
|
500
|
+
color: var(--ink-soft);
|
|
501
|
+
}
|
|
502
|
+
.atelic-ui .rc-legend-foot {
|
|
503
|
+
justify-content: flex-end;
|
|
504
|
+
margin: 10px 0 0;
|
|
505
|
+
padding: 0 4px;
|
|
506
|
+
}
|
|
507
|
+
.atelic-ui .rc-legend li {
|
|
508
|
+
display: inline-flex;
|
|
509
|
+
align-items: center;
|
|
510
|
+
gap: 7px;
|
|
511
|
+
}
|
|
512
|
+
.atelic-ui .rc-legend .op-grade {
|
|
513
|
+
display: inline-grid;
|
|
514
|
+
place-items: center;
|
|
515
|
+
width: 22px;
|
|
516
|
+
height: 22px;
|
|
517
|
+
margin-left: 0;
|
|
518
|
+
padding: 0;
|
|
519
|
+
border-radius: 6px;
|
|
520
|
+
font-size: 12px;
|
|
521
|
+
line-height: 1;
|
|
522
|
+
}
|
|
523
|
+
.atelic-ui .rc-card {
|
|
524
|
+
display: grid;
|
|
525
|
+
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
526
|
+
background: var(--ui-card, var(--card));
|
|
527
|
+
border: 1px solid var(--neutral-100);
|
|
528
|
+
border-radius: var(--radius-md);
|
|
529
|
+
box-shadow: var(--shadow-sm, none);
|
|
530
|
+
overflow: hidden;
|
|
531
|
+
}
|
|
532
|
+
.atelic-ui .rc-group {
|
|
533
|
+
grid-column: 1 / -1;
|
|
534
|
+
margin: 0;
|
|
535
|
+
padding: 7px 16px;
|
|
536
|
+
background: var(--surface-alt, var(--neutral-100));
|
|
537
|
+
font-family: var(--font-mono);
|
|
538
|
+
font-size: 11.5px;
|
|
539
|
+
font-weight: 500;
|
|
540
|
+
letter-spacing: 0.1em;
|
|
541
|
+
text-transform: uppercase;
|
|
542
|
+
color: var(--ink-soft);
|
|
543
|
+
}
|
|
544
|
+
/* A row is a subgrid of the card, so its parts line up down the card. On
|
|
545
|
+
a phone the name sits over the line; wide, they sit side by side. The
|
|
546
|
+
name's link stretches over the whole row. */
|
|
547
|
+
.atelic-ui .rc-row {
|
|
548
|
+
position: relative;
|
|
549
|
+
grid-column: 1 / -1;
|
|
550
|
+
display: grid;
|
|
551
|
+
grid-template-columns: subgrid;
|
|
552
|
+
grid-template-rows: auto auto;
|
|
553
|
+
column-gap: 12px;
|
|
554
|
+
row-gap: 2px;
|
|
555
|
+
align-items: center;
|
|
556
|
+
align-content: center;
|
|
557
|
+
padding: 12px 12px 12px 16px;
|
|
558
|
+
border-top: 1px solid var(--neutral-100);
|
|
559
|
+
transition: background-color 120ms ease;
|
|
560
|
+
}
|
|
561
|
+
.atelic-ui .rc-grade {
|
|
562
|
+
grid-row: 1 / span 2;
|
|
563
|
+
display: inline-flex;
|
|
564
|
+
align-items: center;
|
|
565
|
+
gap: 6px;
|
|
566
|
+
}
|
|
567
|
+
.atelic-ui .rc-grade .op-grade {
|
|
568
|
+
display: inline-grid;
|
|
569
|
+
place-items: center;
|
|
570
|
+
width: 30px;
|
|
571
|
+
height: 30px;
|
|
572
|
+
margin-left: 0;
|
|
573
|
+
padding: 0;
|
|
574
|
+
border-radius: 9px;
|
|
575
|
+
font-size: 15px;
|
|
576
|
+
line-height: 1;
|
|
577
|
+
}
|
|
578
|
+
.atelic-ui .rc-before .op-grade {
|
|
579
|
+
opacity: 0.45;
|
|
580
|
+
}
|
|
581
|
+
.atelic-ui .rc-dash {
|
|
582
|
+
display: inline-flex;
|
|
583
|
+
align-items: center;
|
|
584
|
+
justify-content: center;
|
|
585
|
+
width: 30px;
|
|
586
|
+
height: 30px;
|
|
587
|
+
color: var(--neutral-500);
|
|
588
|
+
}
|
|
589
|
+
.atelic-ui .rc-dash::before {
|
|
590
|
+
content: "";
|
|
591
|
+
width: 12px;
|
|
592
|
+
border-top: 2px solid currentColor;
|
|
593
|
+
}
|
|
594
|
+
.atelic-ui .rc-name {
|
|
595
|
+
grid-column: 2;
|
|
596
|
+
font-size: 16px;
|
|
597
|
+
font-weight: 650;
|
|
598
|
+
letter-spacing: -0.01em;
|
|
599
|
+
line-height: 1.35;
|
|
600
|
+
color: var(--ink);
|
|
601
|
+
}
|
|
602
|
+
.atelic-ui .rc-name a {
|
|
603
|
+
color: inherit;
|
|
604
|
+
text-decoration: none;
|
|
605
|
+
}
|
|
606
|
+
.atelic-ui .rc-name a::after {
|
|
607
|
+
content: "";
|
|
608
|
+
position: absolute;
|
|
609
|
+
inset: 0;
|
|
610
|
+
}
|
|
611
|
+
.atelic-ui .rc-line {
|
|
612
|
+
grid-column: 2;
|
|
613
|
+
font-size: 15px;
|
|
614
|
+
line-height: 1.45;
|
|
615
|
+
color: var(--ink-soft);
|
|
616
|
+
text-wrap: pretty;
|
|
617
|
+
}
|
|
618
|
+
.atelic-ui .rc-line a {
|
|
619
|
+
position: relative;
|
|
620
|
+
z-index: 1;
|
|
621
|
+
color: var(--ui-primary, var(--primary));
|
|
622
|
+
text-decoration: underline;
|
|
623
|
+
text-underline-offset: 2px;
|
|
624
|
+
}
|
|
625
|
+
.atelic-ui .rc-go {
|
|
626
|
+
grid-column: 3;
|
|
627
|
+
grid-row: 1 / span 2;
|
|
628
|
+
color: var(--neutral-500);
|
|
629
|
+
}
|
|
630
|
+
.atelic-ui .rc-sr {
|
|
631
|
+
position: absolute;
|
|
632
|
+
width: 1px;
|
|
633
|
+
height: 1px;
|
|
634
|
+
margin: -1px;
|
|
635
|
+
padding: 0;
|
|
636
|
+
overflow: hidden;
|
|
637
|
+
clip: rect(0, 0, 0, 0);
|
|
638
|
+
white-space: nowrap;
|
|
639
|
+
border: 0;
|
|
640
|
+
}
|
|
641
|
+
@media print, (min-width: 721px) {
|
|
642
|
+
.atelic-ui .rc-card {
|
|
643
|
+
grid-template-columns: auto 232px minmax(0, 1fr) auto;
|
|
644
|
+
}
|
|
645
|
+
.atelic-ui .rc-group {
|
|
646
|
+
padding: 7px 20px;
|
|
647
|
+
}
|
|
648
|
+
.atelic-ui .rc-row {
|
|
649
|
+
grid-template-rows: auto;
|
|
650
|
+
column-gap: 16px;
|
|
651
|
+
padding: 11px 14px 11px 20px;
|
|
652
|
+
}
|
|
653
|
+
.atelic-ui .rc-grade,
|
|
654
|
+
.atelic-ui .rc-go {
|
|
655
|
+
grid-row: 1;
|
|
656
|
+
}
|
|
657
|
+
.atelic-ui .rc-line {
|
|
658
|
+
grid-column: 3;
|
|
659
|
+
}
|
|
660
|
+
.atelic-ui .rc-go {
|
|
661
|
+
grid-column: 4;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
@media print {
|
|
665
|
+
.atelic-ui .rc-card {
|
|
666
|
+
box-shadow: none;
|
|
667
|
+
border-color: var(--neutral-200);
|
|
668
|
+
}
|
|
669
|
+
.atelic-ui .rc-go {
|
|
670
|
+
display: none;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
@media (prefers-reduced-motion: reduce) {
|
|
674
|
+
.atelic-ui .rc-row {
|
|
675
|
+
transition: none;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
.atelic-ui .rc-row:has(.rc-name a):hover,
|
|
679
|
+
.atelic-ui .rc-row:has(.rc-name a):focus-within {
|
|
680
|
+
background: color-mix(in srgb, var(--ui-primary, var(--primary)) 6%, transparent);
|
|
681
|
+
}
|
|
682
|
+
.atelic-ui .rc-row:has(.rc-name a):hover .rc-name,
|
|
683
|
+
.atelic-ui .rc-row:has(.rc-name a):focus-within .rc-name,
|
|
684
|
+
.atelic-ui .rc-row:has(.rc-name a):hover .rc-go,
|
|
685
|
+
.atelic-ui .rc-row:has(.rc-name a):focus-within .rc-go {
|
|
686
|
+
color: var(--ui-primary, var(--primary));
|
|
687
|
+
}
|
|
688
|
+
|
|
420
689
|
/* ---- The charts' shared key: a swatch, a label, a figure ---- */
|
|
421
690
|
.atelic-ui .chart-key {
|
|
422
691
|
list-style: none;
|
|
@@ -447,6 +716,24 @@
|
|
|
447
716
|
flex: none;
|
|
448
717
|
}
|
|
449
718
|
|
|
719
|
+
/* ---- Rank: a figure with its badge; the figure's slot is a fixed
|
|
720
|
+
width, so every rank in a column is the same width and the badges
|
|
721
|
+
line up down it ---- */
|
|
722
|
+
.atelic-ui .rank {
|
|
723
|
+
display: inline-grid;
|
|
724
|
+
grid-template-columns: auto 2.6em;
|
|
725
|
+
column-gap: 8px;
|
|
726
|
+
align-items: center;
|
|
727
|
+
}
|
|
728
|
+
.atelic-ui .rank .chart-swatch {
|
|
729
|
+
justify-self: start;
|
|
730
|
+
}
|
|
731
|
+
.atelic-ui .rank > a,
|
|
732
|
+
.atelic-ui .rank > span:last-child {
|
|
733
|
+
justify-self: end;
|
|
734
|
+
font-variant-numeric: tabular-nums;
|
|
735
|
+
}
|
|
736
|
+
|
|
450
737
|
/* ---- StackedBar: one bar, the parts of a whole ---- */
|
|
451
738
|
.atelic-ui .stacked-bar-track {
|
|
452
739
|
display: flex;
|