Styling and astro refactor of sidenotes
This commit is contained in:
@@ -12,10 +12,12 @@ const { footnotes } = Astro.props
|
|||||||
<ol>
|
<ol>
|
||||||
{footnotes.map((footnote) => (
|
{footnotes.map((footnote) => (
|
||||||
<li id={`fn-${footnote.n}`}>
|
<li id={`fn-${footnote.n}`}>
|
||||||
|
<p class="footnotes__note">
|
||||||
<Node node={footnote.children} />{' '}
|
<Node node={footnote.children} />{' '}
|
||||||
<a href={`#fnref-${footnote.n}`} class="footnotes__back" aria-label="Back to text">
|
<a href={`#fnref-${footnote.n}`} class="footnotes__back" aria-label="Back to text">
|
||||||
↩
|
↩
|
||||||
</a>
|
</a>
|
||||||
|
</p>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ol>
|
</ol>
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
---
|
|
||||||
import Node from '@/components/markdoc/Node.astro'
|
|
||||||
import type { Sidenote } from '@/lib/markdoc/types'
|
|
||||||
|
|
||||||
type Props = { sidenotes: Sidenote[] }
|
|
||||||
|
|
||||||
const { sidenotes } = Astro.props
|
|
||||||
---
|
|
||||||
|
|
||||||
{sidenotes.map((sidenote) => (
|
|
||||||
<aside
|
|
||||||
class:list={['sidenote', `sidenote--${sidenote.type}`]}
|
|
||||||
id={`sn-${sidenote.n}`}
|
|
||||||
style={`position-anchor: --sn-${sidenote.n}`}
|
|
||||||
role="note"
|
|
||||||
>
|
|
||||||
<a class="marker" href={`#snref-${sidenote.n}`} aria-label="Back to the text">
|
|
||||||
{sidenote.marker}
|
|
||||||
</a>
|
|
||||||
<p class="note">
|
|
||||||
{sidenote.type !== 'default' && <span class="kind">{sidenote.type} ⟫ </span>}
|
|
||||||
<Node node={sidenote.children} />
|
|
||||||
</p>
|
|
||||||
</aside>
|
|
||||||
))}
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.sidenote {
|
|
||||||
@mixin typo_body-small;
|
|
||||||
@mixin my var(--space-8);
|
|
||||||
|
|
||||||
scroll-margin-top: calc(var(--layout-header-height) + var(--size-8));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The marker hangs: it lines the note up with the text it belongs to. */
|
|
||||||
.marker {
|
|
||||||
@mixin me var(--size-2);
|
|
||||||
|
|
||||||
float: inline-start;
|
|
||||||
font-family: var(--font-mono);
|
|
||||||
}
|
|
||||||
|
|
||||||
.note {
|
|
||||||
@mixin ma var(--size-0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stacked under the article: a list, separated by rules. */
|
|
||||||
@media (--bp-tablet-down) {
|
|
||||||
.sidenote {
|
|
||||||
@mixin pt var(--space-4);
|
|
||||||
@mixin border-t var(--size-1), solid, var(--color-border-subtle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (--bp-desktop) {
|
|
||||||
@supports (anchor-name: --x) {
|
|
||||||
.sidenote {
|
|
||||||
@mixin my var(--size-0);
|
|
||||||
|
|
||||||
position: absolute;
|
|
||||||
top: anchor(top);
|
|
||||||
left: anchor(--margin-column left);
|
|
||||||
width: anchor-size(--margin-column width);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -98,30 +98,43 @@ for (const [key, value] of Object.entries(crucible)) {
|
|||||||
|
|
||||||
## Tables
|
## Tables
|
||||||
|
|
||||||
{% tableWrapper variant="default" caption="A wrapped table with a caption" %}
|
{% tableWrapper
|
||||||
|
variant="default"
|
||||||
|
caption="Stages of the Work"
|
||||||
|
columns=["rowheader", "key", "num", "num", "text"] %}
|
||||||
{% table %}
|
{% table %}
|
||||||
- Stage
|
- Stage
|
||||||
|
- No
|
||||||
- Heat
|
- Heat
|
||||||
|
- Hours
|
||||||
- Notes
|
- Notes
|
||||||
---
|
---
|
||||||
- Prima Materia
|
- Prima Materia
|
||||||
|
- I
|
||||||
- 3
|
- 3
|
||||||
- Longer cell text to see how wrapping behaves in a data cell
|
- 0.25
|
||||||
|
- The undifferentiated ground; nothing is named yet.
|
||||||
|
---
|
||||||
|
- Calcination
|
||||||
|
- II
|
||||||
|
- 7
|
||||||
|
- 1.50
|
||||||
|
- Burned down to what will not burn.
|
||||||
---
|
---
|
||||||
- Solutio
|
- Solutio
|
||||||
|
- III
|
||||||
- 5
|
- 5
|
||||||
- Short
|
- 12
|
||||||
|
- Dissolved back into flow; forms loosen.
|
||||||
|
---
|
||||||
|
- Tincture
|
||||||
|
- VII
|
||||||
|
- 10
|
||||||
|
- 120.75
|
||||||
|
- The finished colour, carried into everything it touches.
|
||||||
{% /table %}
|
{% /table %}
|
||||||
{% /tableWrapper %}
|
{% /tableWrapper %}
|
||||||
|
|
||||||
{% table %}
|
|
||||||
- Column A
|
|
||||||
- Column B
|
|
||||||
---
|
|
||||||
- A bare table
|
|
||||||
- No wrapper
|
|
||||||
{% /table %}
|
|
||||||
|
|
||||||
## Images
|
## Images
|
||||||
|
|
||||||
{% figure
|
{% figure
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { fields } from '@keystatic/core'
|
import { fields } from '@keystatic/core'
|
||||||
import { wrapper } from '@keystatic/core/content-components'
|
import { wrapper } from '@keystatic/core/content-components'
|
||||||
|
|
||||||
import { TABLE_VARIANTS } from '@/lib/constants/blocks/table'
|
import { COLUMN_TYPES, TABLE_VARIANTS } from '@/lib/constants/blocks/table'
|
||||||
|
|
||||||
export const tableWrapper = wrapper({
|
export const tableWrapper = wrapper({
|
||||||
label: 'Table Wrapper',
|
label: 'Table Wrapper',
|
||||||
@@ -15,5 +15,17 @@ export const tableWrapper = wrapper({
|
|||||||
caption: fields.text({
|
caption: fields.text({
|
||||||
label: 'Caption',
|
label: 'Caption',
|
||||||
}),
|
}),
|
||||||
|
columns: fields.array(
|
||||||
|
fields.select({
|
||||||
|
label: 'Type',
|
||||||
|
options: COLUMN_TYPES,
|
||||||
|
defaultValue: 'default',
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
label: 'Columns',
|
||||||
|
description: 'One per column, left to right.',
|
||||||
|
itemLabel: (item) => item.value,
|
||||||
|
},
|
||||||
|
),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,13 +4,34 @@ export const TABLE_VARIANTS = [
|
|||||||
value: 'default',
|
value: 'default',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Random',
|
label: 'Matrix',
|
||||||
value: 'random',
|
value: 'matrix',
|
||||||
},
|
},
|
||||||
{
|
] as const
|
||||||
label: 'Icon',
|
|
||||||
value: 'icon',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
export type TableVariant = (typeof TABLE_VARIANTS)[number]['value']
|
export type TableVariant = (typeof TABLE_VARIANTS)[number]['value']
|
||||||
|
|
||||||
|
export const COLUMN_TYPES = [
|
||||||
|
{
|
||||||
|
label: 'Default',
|
||||||
|
value: 'default',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Number',
|
||||||
|
value: 'num',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Key',
|
||||||
|
value: 'key',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Sentence',
|
||||||
|
value: 'text',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Row header',
|
||||||
|
value: 'rowheader',
|
||||||
|
},
|
||||||
|
] as const
|
||||||
|
|
||||||
|
export type ColumnType = (typeof COLUMN_TYPES)[number]['value']
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export async function render(
|
|||||||
headings: [],
|
headings: [],
|
||||||
ids: new Set(),
|
ids: new Set(),
|
||||||
footnotes: [],
|
footnotes: [],
|
||||||
sidenotes: [],
|
sidenoteCount: 0,
|
||||||
markers: new Set(),
|
markers: new Set(),
|
||||||
problems: [],
|
problems: [],
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,6 @@ export async function render(
|
|||||||
tree,
|
tree,
|
||||||
headings: state.headings,
|
headings: state.headings,
|
||||||
footnotes: state.footnotes,
|
footnotes: state.footnotes,
|
||||||
sidenotes: state.sidenotes,
|
|
||||||
diagnostics: problems.list,
|
diagnostics: problems.list,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,9 +95,8 @@ export function createTags(state: NodeState, resolveLink: ResolveLink): Record<s
|
|||||||
message: `Two sidenotes use the marker "${marker}".`,
|
message: `Two sidenotes use the marker "${marker}".`,
|
||||||
})
|
})
|
||||||
state.markers.add(marker)
|
state.markers.add(marker)
|
||||||
const n = state.sidenotes.length + 1
|
const n = (state.sidenoteCount += 1)
|
||||||
const children = inlineOf(text(node.attributes.note))
|
const children = inlineOf(text(node.attributes.note))
|
||||||
state.sidenotes.push({ n, marker, type: noteType, children })
|
|
||||||
return [
|
return [
|
||||||
new Tag('a', { class: 'sidenote-ref', id: `snref-${n}`, href: `#sn-${n}` }, [marker]),
|
new Tag('a', { class: 'sidenote-ref', id: `snref-${n}`, href: `#sn-${n}` }, [marker]),
|
||||||
new Tag(
|
new Tag(
|
||||||
@@ -136,15 +135,33 @@ export function createTags(state: NodeState, resolveLink: ResolveLink): Record<s
|
|||||||
},
|
},
|
||||||
tableWrapper: {
|
tableWrapper: {
|
||||||
...generated.tableWrapper,
|
...generated.tableWrapper,
|
||||||
|
/* Keystatic makes an array attribute required; a table without column
|
||||||
|
types is the normal case. */
|
||||||
|
attributes: {
|
||||||
|
...generated.tableWrapper?.attributes,
|
||||||
|
columns: { ...generated.tableWrapper?.attributes?.columns, required: false },
|
||||||
|
},
|
||||||
|
transform(node, config) {
|
||||||
|
const { columns, ...attributes } = node.transformAttributes(config) as {
|
||||||
|
columns?: string[]
|
||||||
|
}
|
||||||
|
const children = node.transformChildren(config)
|
||||||
|
if (columns?.length) applyColumns(children, columns, state)
|
||||||
|
return new Tag('TableWrapper', attributes, children)
|
||||||
|
},
|
||||||
|
/* The children of a Keystatic wrapper are free-form block content, so
|
||||||
|
"exactly one table" can only be a build-time check. */
|
||||||
validate(node) {
|
validate(node) {
|
||||||
const tables = node.children.filter((child) => child.tag === 'table').length
|
const tables = node.children.filter(
|
||||||
|
(child) => child.type === 'table' || child.tag === 'table',
|
||||||
|
).length
|
||||||
return tables === 1
|
return tables === 1
|
||||||
? []
|
? []
|
||||||
: [
|
: [
|
||||||
{
|
{
|
||||||
id: 'table-wrapper-children',
|
id: 'table-wrapper-children',
|
||||||
level: 'warning' as const,
|
level: 'error' as const,
|
||||||
message: `A table wrapper should hold exactly one table, this one holds ${tables}.`,
|
message: `A table wrapper holds exactly one table, this one holds ${tables}.`,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,13 +13,6 @@ export type Footnote = {
|
|||||||
children: RenderableTreeNode[]
|
children: RenderableTreeNode[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Sidenote = {
|
|
||||||
n: number
|
|
||||||
marker: string
|
|
||||||
type: string
|
|
||||||
children: RenderableTreeNode[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Problem = {
|
export type Problem = {
|
||||||
level: Level
|
level: Level
|
||||||
message: string
|
message: string
|
||||||
@@ -29,7 +22,7 @@ export type NodeState = {
|
|||||||
headings: Heading[]
|
headings: Heading[]
|
||||||
ids: Set<string>
|
ids: Set<string>
|
||||||
footnotes: Footnote[]
|
footnotes: Footnote[]
|
||||||
sidenotes: Sidenote[]
|
sidenoteCount: number
|
||||||
markers: Set<string>
|
markers: Set<string>
|
||||||
problems: Problem[]
|
problems: Problem[]
|
||||||
}
|
}
|
||||||
@@ -42,7 +35,6 @@ export type Rendered = {
|
|||||||
tree: RenderableTreeNode
|
tree: RenderableTreeNode
|
||||||
headings: Heading[]
|
headings: Heading[]
|
||||||
footnotes: Footnote[]
|
footnotes: Footnote[]
|
||||||
sidenotes: Sidenote[]
|
|
||||||
diagnostics: Diagnostic[]
|
diagnostics: Diagnostic[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
109
src/styles/components/footnote.css
Normal file
109
src/styles/components/footnote.css
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
@layer components.blocks {
|
||||||
|
.footnote-ref {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.7em;
|
||||||
|
font-weight: var(--font-weight-emphasis);
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
& a {
|
||||||
|
@mixin px var(--size-1);
|
||||||
|
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
@mixin focus-ring;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footnotes {
|
||||||
|
--footnote-marker: 1.5rem;
|
||||||
|
|
||||||
|
@mixin mt var(--space-10);
|
||||||
|
@mixin pt var(--space-6);
|
||||||
|
@mixin border-t var(--border-boundary), solid, var(--color-border-strong);
|
||||||
|
|
||||||
|
counter-reset: fn;
|
||||||
|
|
||||||
|
& .footnotes__title {
|
||||||
|
@mixin typo_label;
|
||||||
|
@mixin mt var(--size-0);
|
||||||
|
@mixin mb var(--size-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
& ol {
|
||||||
|
@mixin ma var(--size-0);
|
||||||
|
@mixin pl var(--size-0);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--size-4);
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
& li {
|
||||||
|
@mixin typo_body-small;
|
||||||
|
|
||||||
|
counter-increment: fn;
|
||||||
|
scroll-margin-block-start: calc(var(--layout-header-height) + var(--size-8));
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: var(--footnote-marker) minmax(0, 1fr);
|
||||||
|
column-gap: var(--slot-prose-gap);
|
||||||
|
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: counter(fn);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-weight: var(--font-weight-emphasis);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:target {
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& .footnotes__note {
|
||||||
|
@mixin ma var(--size-0);
|
||||||
|
}
|
||||||
|
|
||||||
|
& a {
|
||||||
|
color: var(--color-text-link);
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-thickness: var(--size-1);
|
||||||
|
text-underline-offset: var(--size-2);
|
||||||
|
|
||||||
|
&:visited {
|
||||||
|
color: var(--color-text-link-visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-text-link-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
@mixin focus-ring;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& a.footnotes__back {
|
||||||
|
color: var(--color-text-quiet);
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,3 +3,6 @@
|
|||||||
@import url("lead.css");
|
@import url("lead.css");
|
||||||
@import url("callout.css");
|
@import url("callout.css");
|
||||||
@import url("reveal.css");
|
@import url("reveal.css");
|
||||||
|
@import url("table-wrapper.css");
|
||||||
|
@import url("footnote.css");
|
||||||
|
@import url("sidenote.css");
|
||||||
|
|||||||
@@ -335,23 +335,5 @@
|
|||||||
@mixin border-t var(--border-card), solid, var(--color-border-strong);
|
@mixin border-t var(--border-card), solid, var(--color-border-strong);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
& .num {
|
|
||||||
font-variant-numeric: tabular-nums;
|
|
||||||
text-align: right;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
& .key {
|
|
||||||
font-weight: var(--font-weight-emphasis);
|
|
||||||
}
|
|
||||||
|
|
||||||
td.text {
|
|
||||||
font-family: var(--font-body);
|
|
||||||
font-size: 1.077em;
|
|
||||||
line-height: 1.55;
|
|
||||||
color: var(--color-text-secondary);
|
|
||||||
text-wrap: pretty;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
80
src/styles/components/sidenote.css
Normal file
80
src/styles/components/sidenote.css
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
@layer components.blocks {
|
||||||
|
.sidenote-ref {
|
||||||
|
@mixin px var(--size-1);
|
||||||
|
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.75em;
|
||||||
|
font-weight: var(--font-weight-emphasis);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
@mixin focus-ring;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidenote {
|
||||||
|
@mixin typo_body-small;
|
||||||
|
@mixin pt var(--size-3);
|
||||||
|
@mixin border-t var(--border-card), solid, var(--color-border-strong);
|
||||||
|
|
||||||
|
scroll-margin-block-start: calc(var(--layout-header-height) + var(--size-8));
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
|
||||||
|
/* Hangs, so the first line starts against the marker. */
|
||||||
|
& .sidenote__marker {
|
||||||
|
@mixin me var(--size-4);
|
||||||
|
|
||||||
|
float: inline-start;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-weight: var(--font-weight-emphasis);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:target {
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
|
||||||
|
& .sidenote__marker {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidenote--optional {
|
||||||
|
@mixin border-t var(--border-hairline), solid, var(--color-border-normal);
|
||||||
|
|
||||||
|
color: var(--color-text-tertiary);
|
||||||
|
|
||||||
|
& .sidenote__marker {
|
||||||
|
color: var(--color-text-quiet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidenote--example {
|
||||||
|
& .sidenote__marker {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (--bp-tablet-down) {
|
||||||
|
.sidenote {
|
||||||
|
@mixin my var(--space-6);
|
||||||
|
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (--bp-desktop) {
|
||||||
|
.sidenote {
|
||||||
|
@mixin my var(--size-0);
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
inset-inline-start: calc(100% + var(--layout-gutter));
|
||||||
|
width: var(--layout-margin-width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
121
src/styles/components/table-wrapper.css
Normal file
121
src/styles/components/table-wrapper.css
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
@layer components.blocks {
|
||||||
|
.table-wrapper {
|
||||||
|
/* The header treatment. Ink by default: a standalone wrapper is a
|
||||||
|
citable object. Position overrides these — nothing is authored. */
|
||||||
|
--table-header-fg: var(--color-text-inverse);
|
||||||
|
--table-header-bg: var(--color-surface-inverse);
|
||||||
|
--table-header-rule: 0;
|
||||||
|
--table-header-edge: var(--color-border-normal);
|
||||||
|
|
||||||
|
@mixin my var(--space-10);
|
||||||
|
|
||||||
|
counter-increment: tbl;
|
||||||
|
|
||||||
|
& figcaption {
|
||||||
|
@mixin typo_label;
|
||||||
|
@mixin mb var(--size-3);
|
||||||
|
display: flex;
|
||||||
|
gap: var(--size-5);
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "Table " counter(tbl, upper-roman) " ⟫";
|
||||||
|
font-weight: var(--font-weight-emphasis);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& table {
|
||||||
|
@mixin my var(--size-0);
|
||||||
|
|
||||||
|
& thead {
|
||||||
|
& th {
|
||||||
|
border-block-end: var(--table-header-rule);
|
||||||
|
color: var(--table-header-fg);
|
||||||
|
background-color: var(--table-header-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& td {
|
||||||
|
&.text {
|
||||||
|
font-family: var(--font-body);
|
||||||
|
font-size: 1.077em;
|
||||||
|
line-height: 1.55;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
text-wrap: pretty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& .num {
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
text-align: right;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .key {
|
||||||
|
font-weight: var(--font-weight-emphasis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Underline header: a table introduced by a heading, or one inside a
|
||||||
|
block, is part of the argument rather than a reference. A block with
|
||||||
|
its own surface redeclares --table-header-bg so the sticky first
|
||||||
|
column on mobile paints the right colour. */
|
||||||
|
|
||||||
|
.prose :is(h2, h3, h4) + .table-wrapper,
|
||||||
|
:is(.callout, .reveal) .table-wrapper {
|
||||||
|
--table-header-fg: inherit;
|
||||||
|
--table-header-bg: var(--color-surface-base);
|
||||||
|
--table-header-rule: var(--border-card) solid var(--color-border-strong);
|
||||||
|
--table-header-edge: var(--color-border-strong);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === MATRIX ===
|
||||||
|
A correspondence table: the cells carry hits, not values, so it has to
|
||||||
|
read as a grid — rules on both axes, everything centred, and a miss
|
||||||
|
stated rather than left looking like an oversight. */
|
||||||
|
|
||||||
|
.table-wrapper--matrix {
|
||||||
|
& :is(th, td) {
|
||||||
|
@mixin px var(--space-4);
|
||||||
|
@mixin border-r var(--border-hairline), solid, var(--color-border-subtle);
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
& :is(th, td):last-child {
|
||||||
|
border-inline-end: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The first column is the subject; it keeps its reading direction. */
|
||||||
|
& :is(thead, tbody) :is(th, td):first-child {
|
||||||
|
@mixin border-r var(--border-card), solid, var(--color-border-strong);
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Axis labels are short. Let them wrap instead of widening a column. */
|
||||||
|
& thead th {
|
||||||
|
text-wrap: balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A hit: the cell itself is the mark. */
|
||||||
|
& tbody td.is-set {
|
||||||
|
background-color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A miss, said out loud. */
|
||||||
|
& tbody td:empty::before {
|
||||||
|
content: "·";
|
||||||
|
color: var(--color-text-quiet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (--bp-mobile) {
|
||||||
|
.table-wrapper .table-scroll thead th:first-child {
|
||||||
|
border-inline-end-color: var(--table-header-edge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@define-mixin ms $spacing {
|
@define-mixin ms $spacing {
|
||||||
margin-line-start: $spacing;
|
margin-inline-start: $spacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
@define-mixin me $spacing {
|
@define-mixin me $spacing {
|
||||||
|
|||||||
Reference in New Issue
Block a user