@dataimago/css 0.1.0-alpha.22 → 0.1.0-alpha.24
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 +70 -0
- package/dist/bin/dataimago-sync.mjs +19 -1
- package/dist/dataviz/dataimago-dataviz.css +1 -1
- package/dist/dataviz/dataimago-dataviz.json +2 -2
- package/dist/dataviz/dataimago-dataviz.tex +1 -1
- package/dist/dataviz/dataimago_dataviz.R +1 -1
- package/dist/figures/dataimago-figure.sty +1 -1
- package/dist/figures/dataimago-tokens.sty +1 -1
- package/dist/fonts/cyrillic/fonts.css +1 -1
- package/dist/fonts/cyrillic-ext/fonts.css +1 -1
- package/dist/fonts/greek/fonts.css +1 -1
- package/dist/fonts/greek-ext/fonts.css +1 -1
- package/dist/fonts/josefin/fonts.css +1 -1
- package/dist/fonts/lang.css +1 -1
- package/dist/fonts/latin/fonts.css +1 -1
- package/dist/fonts/latin-ext/fonts.css +1 -1
- package/dist/fonts/math/fonts.css +1 -1
- package/dist/fonts/pkg/fonts.css +1 -1
- package/dist/fonts/vietnamese/fonts.css +1 -1
- package/dist/latex/dataimago-tokens.sty +1 -1
- package/dist/latex/dataimago.sty +1 -1
- package/dist/outputs.json +53 -44
- package/dist/quarto/_extensions/dataimago/monograph/_extension.yml +1 -1
- package/dist/quarto/_extensions/dataimago/monograph/dataimago-base.sty +1 -1
- package/dist/quarto/_extensions/dataimago/monograph/dataimago-monograph.sty +1 -1
- package/dist/quarto/_extensions/dataimago/monograph/dataimago-tokens.sty +1 -1
- package/dist/quarto/_extensions/dataimago/monograph/filters/figure-plate.lua +1 -1
- package/dist/quarto/_extensions/dataimago/monograph/filters/matter.lua +1 -1
- package/dist/quarto/_extensions/dataimago/monograph/filters/pkg-name.lua +1 -1
- package/dist/quarto/_extensions/dataimago/monograph/partials/before-body.tex +1 -1
- package/dist/quarto/_extensions/dataimago/monograph/partials/title.tex +1 -1
- package/dist/quarto/_extensions/dataimago/pkgsite/_extension.yml +28 -0
- package/dist/quarto/_extensions/dataimago/pkgsite/components.scss +243 -0
- package/dist/quarto/_extensions/dataimago/pkgsite/dataimago-dark.scss +344 -0
- package/dist/quarto/_extensions/dataimago/pkgsite/dataimago-light.scss +338 -0
- package/dist/quarto/_extensions/dataimago/pkgsite/filters/landing.lua +251 -0
- package/dist/quarto/_extensions/dataimago/pkgsite/filters/provenance.lua +212 -0
- package/dist/quarto/_extensions/dataimago/pkgsite/hero-fallback.svg +53 -0
- package/dist/quarto/_extensions/dataimago/pkgsite/pkgsite-hero.js +99 -0
- package/dist/quarto/_extensions/dataimago/pkgsite/pkgsite.scss +281 -0
- package/dist/quarto/components.scss +1 -1
- package/dist/quarto/dataimago-dark.scss +1 -1
- package/dist/quarto/dataimago-light.scss +1 -1
- package/dist/templates/KNOWLEDGE-explorer.md +1 -1
- package/dist/templates/KNOWLEDGE-framework.md +1 -1
- package/dist/templates/KNOWLEDGE-research.md +1 -1
- package/dist/templates/quarto_website/README.md +4 -2
- package/dist/templates/quarto_website/_quarto.yml.template +1 -1
- package/dist/templates/quarto_website/about.qmd.template +1 -1
- package/dist/templates/quarto_website/assets/css/lang.css +1 -1
- package/dist/templates/quarto_website/assets/js/custom-anchors.js +1 -1
- package/dist/templates/quarto_website/assets/js/logo-switch.js +1 -1
- package/dist/templates/quarto_website/assets/js/scroll.js +1 -1
- package/dist/templates/quarto_website/ethics.qmd.template +1 -1
- package/dist/templates/quarto_website/index.qmd.template +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
-- @dataimago/css@0.1.0-alpha.24 dist/quarto/_extensions/dataimago/pkgsite/filters/provenance.lua · sha256:68e33df59cd4c88e1d4bd51bdd3ec45bfa62f64a1b27c8ed305cb59d4e49853b · generated from tokens; do not edit · restore: npx -p @dataimago/css@0.1.0-alpha.24 dataimago-sync
|
|
2
|
+
-- provenance.lua: the AI-composed label. Pandoc 3 Lua filter for Quarto >= 1.8.
|
|
3
|
+
--
|
|
4
|
+
-- A page whose front matter carries `knowledge:` gets a div.di-provenance before its first block; a page
|
|
5
|
+
-- without the key is returned unchanged. The label is how a reader knows the page was drafted by a model and
|
|
6
|
+
-- whether the maintainer has approved it, so the strings are fixed:
|
|
7
|
+
-- draft: AI-composed draft: not yet approved by the maintainer
|
|
8
|
+
-- approved: AI-composed · drafted by {composer} from source {sha7} · {edited and }approved by {who}, {date} · How this page was made
|
|
9
|
+
-- stale: appends " · written against {sha7}; the code has changed since" to whichever label is shown
|
|
10
|
+
--
|
|
11
|
+
-- Rules:
|
|
12
|
+
-- * Every interpolated value is a Pandoc Str, so the HTML writer escapes it.
|
|
13
|
+
-- * source_sha must match ^[0-9a-f]{7,40}$ (shown as its first 7 characters) or the render fails: a malformed
|
|
14
|
+
-- sha is never printed. source_url is linked only if it matches ^https?://; otherwise the sha is unlinked.
|
|
15
|
+
-- * `about` is site-root relative; the href is prefixed with "../" once per directory the page sits below the
|
|
16
|
+
-- project root, and a .qmd extension becomes .html. Absolute paths and URL schemes are not linked.
|
|
17
|
+
-- * status: approved without composer, approved_by or approved_on fails the render: Phase D writes this block, so
|
|
18
|
+
-- a gap is a generator bug, and a silent downgrade to "draft" would hide it. approved_on must be a real
|
|
19
|
+
-- YYYY-MM-DD date; edited_by_author and stale must be YAML booleans (not "true", "yes" or 1).
|
|
20
|
+
-- * Every failure names the page.
|
|
21
|
+
|
|
22
|
+
local stringify = pandoc.utils.stringify
|
|
23
|
+
|
|
24
|
+
local function text(v)
|
|
25
|
+
if v == nil then return '' end
|
|
26
|
+
local t = pandoc.utils.type(v)
|
|
27
|
+
if t == 'string' then return v end
|
|
28
|
+
if t == 'boolean' or t == 'number' then return tostring(v) end
|
|
29
|
+
if t == 'Inlines' then
|
|
30
|
+
local out = {}
|
|
31
|
+
for _, el in ipairs(v) do
|
|
32
|
+
if el.t == 'Str' then out[#out + 1] = el.text
|
|
33
|
+
elseif el.t == 'Space' or el.t == 'SoftBreak' then out[#out + 1] = ' '
|
|
34
|
+
elseif el.t == 'RawInline' or el.t == 'Code' then out[#out + 1] = el.text
|
|
35
|
+
else out[#out + 1] = stringify(el) end
|
|
36
|
+
end
|
|
37
|
+
return table.concat(out)
|
|
38
|
+
end
|
|
39
|
+
return stringify(v)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
local function trim(s) return (s:gsub('^%s+', ''):gsub('%s+$', '')) end
|
|
43
|
+
|
|
44
|
+
local function page_name()
|
|
45
|
+
local input = quarto and quarto.doc and quarto.doc.input_file
|
|
46
|
+
if not input then
|
|
47
|
+
local inputs = PANDOC_STATE and PANDOC_STATE.input_files
|
|
48
|
+
input = inputs and inputs[1]
|
|
49
|
+
end
|
|
50
|
+
if not input then return '(unknown page)' end
|
|
51
|
+
local root = quarto and quarto.project and quarto.project.directory
|
|
52
|
+
if root then
|
|
53
|
+
root = root:gsub('/+$', '')
|
|
54
|
+
if input:sub(1, #root + 1) == root .. '/' then return input:sub(#root + 2) end
|
|
55
|
+
end
|
|
56
|
+
return input
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
local function warn(msg)
|
|
60
|
+
io.stderr:write('provenance.lua: WARNING: ' .. page_name() .. ': ' .. msg .. '\n')
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
-- Quarto's Lua environment logs error() and carries on, which would publish a page with no label (or the
|
|
64
|
+
-- wrong one). Exiting non-zero is what actually refuses the render.
|
|
65
|
+
local function fail(msg)
|
|
66
|
+
io.stderr:write('ERROR: provenance.lua: ' .. page_name() .. ': ' .. msg .. '\n')
|
|
67
|
+
os.exit(1)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
-- plain text -> Str/Space inlines (Pandoc escapes them on output); leading and trailing spaces are kept
|
|
71
|
+
local function words(s)
|
|
72
|
+
local out = pandoc.Inlines({})
|
|
73
|
+
for space, w in s:gmatch('(%s*)(%S+)') do
|
|
74
|
+
if space ~= '' then out:insert(pandoc.Space()) end
|
|
75
|
+
out:insert(pandoc.Str(w))
|
|
76
|
+
end
|
|
77
|
+
if s:match('%s$') then out:insert(pandoc.Space()) end
|
|
78
|
+
return out
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
local function append(dst, src)
|
|
82
|
+
for _, el in ipairs(src) do dst:insert(el) end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
local function dirname(p) return (p:match('^(.*)/[^/]*$')) or '' end
|
|
86
|
+
|
|
87
|
+
-- how many directories below the project root this page is
|
|
88
|
+
local function depth()
|
|
89
|
+
local input = quarto and quarto.doc and quarto.doc.input_file
|
|
90
|
+
if not input then
|
|
91
|
+
local inputs = PANDOC_STATE and PANDOC_STATE.input_files
|
|
92
|
+
input = inputs and inputs[1]
|
|
93
|
+
end
|
|
94
|
+
local root = quarto and quarto.project and quarto.project.directory
|
|
95
|
+
if not input or not root then return 0 end
|
|
96
|
+
root = root:gsub('/+$', '')
|
|
97
|
+
local rel
|
|
98
|
+
if input:sub(1, #root + 1) == root .. '/' then rel = input:sub(#root + 2) else return 0 end
|
|
99
|
+
local dir = dirname(rel)
|
|
100
|
+
if dir == '' then return 0 end
|
|
101
|
+
local n = 0
|
|
102
|
+
for _ in dir:gmatch('[^/]+') do n = n + 1 end
|
|
103
|
+
return n
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
local function about_href(about)
|
|
107
|
+
about = trim(about)
|
|
108
|
+
if about == '' then return nil end
|
|
109
|
+
if about:sub(1, 1) == '/' or about:match('^%a[%w+.%-]*:') then
|
|
110
|
+
warn('knowledge.about must be a site-relative path; "' .. about .. '" is not linked')
|
|
111
|
+
return nil
|
|
112
|
+
end
|
|
113
|
+
for seg in about:gmatch('[^/]+') do
|
|
114
|
+
if seg == '..' then
|
|
115
|
+
warn('knowledge.about must not contain a ".." segment; "' .. about .. '" is not linked')
|
|
116
|
+
return nil
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
-- Quarto does not rewrite links inside a filter-built block, so point at the rendered page
|
|
120
|
+
about = about:gsub('%.qmd$', '.html')
|
|
121
|
+
about = about:gsub('%.qmd([#?])', '.html%1', 1)
|
|
122
|
+
return string.rep('../', depth()) .. about
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
local function sha7(sha)
|
|
126
|
+
sha = trim(sha)
|
|
127
|
+
if not (sha:match('^[0-9a-f]+$') and #sha >= 7 and #sha <= 40) then
|
|
128
|
+
fail('knowledge.source_sha must be 7 to 40 lowercase hex characters; refusing to print it')
|
|
129
|
+
end
|
|
130
|
+
return sha:sub(1, 7)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
-- absent -> false; a YAML boolean -> itself; anything else (including the strings "true", "yes", "1") fails
|
|
134
|
+
local function bool(k, key)
|
|
135
|
+
local v = k[key]
|
|
136
|
+
if v == nil then return false end
|
|
137
|
+
if type(v) == 'boolean' then return v end
|
|
138
|
+
fail('knowledge.' .. key .. ' must be a boolean (true or false), not "' .. text(v) .. '"')
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
local function valid_date(s)
|
|
142
|
+
local y, m, d = s:match('^(%d%d%d%d)-(%d%d)-(%d%d)$')
|
|
143
|
+
if not y then return false end
|
|
144
|
+
y, m, d = tonumber(y), tonumber(m), tonumber(d)
|
|
145
|
+
if m < 1 or m > 12 or d < 1 then return false end
|
|
146
|
+
local days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
|
|
147
|
+
if (y % 4 == 0 and y % 100 ~= 0) or y % 400 == 0 then days[2] = 29 end
|
|
148
|
+
return d <= days[m]
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
function Pandoc(doc)
|
|
152
|
+
local k = doc.meta.knowledge
|
|
153
|
+
if k == nil then return nil end
|
|
154
|
+
if pandoc.utils.type(k) ~= 'table' and pandoc.utils.type(k) ~= 'Map' then
|
|
155
|
+
fail('knowledge: must be a mapping')
|
|
156
|
+
end
|
|
157
|
+
local function get(key) return trim(text(k[key])) end
|
|
158
|
+
|
|
159
|
+
local status = get('status')
|
|
160
|
+
if status ~= 'draft' and status ~= 'approved' then
|
|
161
|
+
fail('knowledge.status must be "draft" or "approved"')
|
|
162
|
+
end
|
|
163
|
+
local stale = bool(k, 'stale')
|
|
164
|
+
local sha_raw = get('source_sha')
|
|
165
|
+
local short
|
|
166
|
+
if sha_raw ~= '' then short = sha7(sha_raw) end
|
|
167
|
+
if (status == 'approved' or stale) and not short then
|
|
168
|
+
fail('knowledge.source_sha is required for an approved or stale page')
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
local approved = status == 'approved'
|
|
172
|
+
local edited = bool(k, 'edited_by_author')
|
|
173
|
+
if approved then
|
|
174
|
+
for _, key in ipairs({ 'composer', 'approved_by', 'approved_on' }) do
|
|
175
|
+
if get(key) == '' then fail('knowledge.status is approved but knowledge.' .. key .. ' is missing') end
|
|
176
|
+
end
|
|
177
|
+
if not valid_date(get('approved_on')) then
|
|
178
|
+
fail('knowledge.approved_on must be a real date written YYYY-MM-DD, not "' .. get('approved_on') .. '"')
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
local inl = pandoc.Inlines({})
|
|
183
|
+
if not approved then
|
|
184
|
+
append(inl, words('AI-composed draft: not yet approved by the maintainer'))
|
|
185
|
+
else
|
|
186
|
+
append(inl, words('AI-composed · drafted by ' .. get('composer') .. ' from source'))
|
|
187
|
+
inl:insert(pandoc.Space())
|
|
188
|
+
local url = get('source_url')
|
|
189
|
+
if url:match('^https?://') then
|
|
190
|
+
inl:insert(pandoc.Link({ pandoc.Str(short) }, url))
|
|
191
|
+
else
|
|
192
|
+
inl:insert(pandoc.Str(short))
|
|
193
|
+
end
|
|
194
|
+
local who = edited and 'edited and approved by ' or 'approved by '
|
|
195
|
+
append(inl, words(' · ' .. who .. get('approved_by') .. ', ' .. get('approved_on')))
|
|
196
|
+
local href = about_href(get('about'))
|
|
197
|
+
if href then
|
|
198
|
+
append(inl, words(' ·'))
|
|
199
|
+
inl:insert(pandoc.Space())
|
|
200
|
+
inl:insert(pandoc.Link(words('How this page was made'), href))
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
if stale then
|
|
204
|
+
append(inl, words(' · written against ' .. short .. '; the code has changed since'))
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
local classes = { 'di-provenance', approved and 'di-provenance-approved' or 'di-provenance-draft' }
|
|
208
|
+
if stale then classes[#classes + 1] = 'di-provenance-stale' end
|
|
209
|
+
local div = pandoc.Div({ pandoc.Para(inl) }, pandoc.Attr('', classes, { { 'role', 'note' } }))
|
|
210
|
+
doc.blocks:insert(1, div)
|
|
211
|
+
return doc
|
|
212
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<!-- @dataimago/css@0.1.0-alpha.24 dist/quarto/_extensions/dataimago/pkgsite/hero-fallback.svg · sha256:60a284fe66f799d46a383ddaba18712ac896aa05cece9b77d28f56e0767309f3 · generated from tokens; do not edit · restore: npx -p @dataimago/css@0.1.0-alpha.24 dataimago-sync -->
|
|
2
|
+
<svg class="di-hero-fallback" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" preserveAspectRatio="xMidYMid meet" focusable="false">
|
|
3
|
+
<style>
|
|
4
|
+
.di-hero-fallback { display: block; width: 100%; height: 100%; }
|
|
5
|
+
.di-hero-fallback .axis { stroke: var(--color-border-moderate, #8a8a85); stroke-width: 2; fill: none; stroke-linecap: round; }
|
|
6
|
+
.di-hero-fallback .grid { stroke: var(--color-border-subtle, #c9c9c4); stroke-width: 1; fill: none; }
|
|
7
|
+
.di-hero-fallback .curve { fill: none; stroke-width: 5; stroke-linecap: round; }
|
|
8
|
+
.di-hero-fallback .c1 { stroke: var(--color-interactive-primary, #1d3b2f); }
|
|
9
|
+
.di-hero-fallback .c2 { stroke: var(--color-dataviz-categorical-1, #0072b2); }
|
|
10
|
+
.di-hero-fallback .c3 { stroke: var(--color-dataviz-categorical-2, #d55e00); }
|
|
11
|
+
.di-hero-fallback .c4 { stroke: var(--color-dataviz-categorical-3, #009e73); }
|
|
12
|
+
.di-hero-fallback .c5 { stroke: var(--color-dataviz-categorical-5, #0072b2); }
|
|
13
|
+
.di-hero-fallback .c6 { stroke: var(--color-interactive-accent, #b45f06); }
|
|
14
|
+
.di-hero-fallback .c7 { stroke: var(--color-content-secondary, #5b5b57); stroke-width: 3; }
|
|
15
|
+
.di-hero-fallback .dot { stroke: none; }
|
|
16
|
+
.di-hero-fallback .d1 { fill: var(--color-interactive-primary, #1d3b2f); }
|
|
17
|
+
.di-hero-fallback .d3 { fill: var(--color-dataviz-categorical-2, #d55e00); }
|
|
18
|
+
.di-hero-fallback .d5 { fill: var(--color-dataviz-categorical-5, #0072b2); }
|
|
19
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
20
|
+
.di-hero-fallback .curve {
|
|
21
|
+
stroke-dasharray: 1;
|
|
22
|
+
stroke-dashoffset: 1;
|
|
23
|
+
animation-name: di-hero-draw;
|
|
24
|
+
animation-duration: 2400ms;
|
|
25
|
+
animation-timing-function: ease-in-out;
|
|
26
|
+
animation-iteration-count: 3;
|
|
27
|
+
animation-fill-mode: both;
|
|
28
|
+
}
|
|
29
|
+
.di-hero-fallback .c2 { animation-delay: 120ms; }
|
|
30
|
+
.di-hero-fallback .c3 { animation-delay: 240ms; }
|
|
31
|
+
.di-hero-fallback .c4 { animation-delay: 360ms; }
|
|
32
|
+
.di-hero-fallback .c5 { animation-delay: 480ms; }
|
|
33
|
+
.di-hero-fallback .c6 { animation-delay: 600ms; }
|
|
34
|
+
.di-hero-fallback .c7 { animation-delay: 720ms; }
|
|
35
|
+
@keyframes di-hero-draw {
|
|
36
|
+
from { stroke-dashoffset: 1; }
|
|
37
|
+
to { stroke-dashoffset: 0; }
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
</style>
|
|
41
|
+
<path class="grid" d="M80 560H720M80 400H720M80 240H720M80 80H720"/>
|
|
42
|
+
<path class="axis" d="M80 80V720H720"/>
|
|
43
|
+
<path class="curve c7" pathLength="1" d="M80 720C240 716 400 690 720 640"/>
|
|
44
|
+
<path class="curve c6" pathLength="1" d="M80 720C230 700 400 640 720 520"/>
|
|
45
|
+
<path class="curve c5" pathLength="1" d="M80 720C220 690 400 590 720 410"/>
|
|
46
|
+
<path class="curve c4" pathLength="1" d="M80 720C210 670 390 540 720 310"/>
|
|
47
|
+
<path class="curve c3" pathLength="1" d="M80 720C190 640 370 470 720 210"/>
|
|
48
|
+
<path class="curve c2" pathLength="1" d="M80 720C170 600 340 400 720 140"/>
|
|
49
|
+
<path class="curve c1" pathLength="1" d="M80 720C150 560 300 330 720 90"/>
|
|
50
|
+
<circle class="dot d1" cx="720" cy="90" r="9"/>
|
|
51
|
+
<circle class="dot d3" cx="720" cy="210" r="7"/>
|
|
52
|
+
<circle class="dot d5" cx="720" cy="410" r="7"/>
|
|
53
|
+
</svg>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// @dataimago/css@0.1.0-alpha.24 dist/quarto/_extensions/dataimago/pkgsite/pkgsite-hero.js · sha256:372b3ff2e24a7f3ce0973f50abc31b491465ba0e973b6cf6f917ea41dd21ea5c · generated from tokens; do not edit · restore: npx -p @dataimago/css@0.1.0-alpha.24 dataimago-sync
|
|
2
|
+
/**
|
|
3
|
+
* pkgsite hero loader (spec "Hero inputs"). Loaded only for `hero.kind: js`.
|
|
4
|
+
*
|
|
5
|
+
* The module at `data-hero-src` is imported only when motion is allowed. Its default export is
|
|
6
|
+
* `mount(el, { signal }) -> { pause(), play(), destroy() }`; a result without pause and play is refused
|
|
7
|
+
* (motion-lint cannot see JS, so the contract is enforced here, at runtime). The poster stays visible until a
|
|
8
|
+
* mount succeeds, and on any failure, so the page never breaks. The module and the poster must come from the
|
|
9
|
+
* site's own origin (a data: poster in a raster type is the one exception).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const REDUCED = '(prefers-reduced-motion: reduce)';
|
|
13
|
+
|
|
14
|
+
const RASTER_DATA = /^data:image\/(png|jpeg|gif|webp)[;,]/i;
|
|
15
|
+
|
|
16
|
+
export function initHeroes({
|
|
17
|
+
document: doc = globalThis.document,
|
|
18
|
+
window: win = globalThis.window,
|
|
19
|
+
importFn = (href) => import(href),
|
|
20
|
+
console: log = globalThis.console,
|
|
21
|
+
} = {}) {
|
|
22
|
+
const none = { destroy() {}, ready: Promise.resolve() };
|
|
23
|
+
if (!doc || !win) return none;
|
|
24
|
+
if (win.matchMedia?.(REDUCED).matches) return none;
|
|
25
|
+
const mounts = [...doc.querySelectorAll('.di-hero-mount')].filter((el) => el.dataset.heroSrc);
|
|
26
|
+
if (mounts.length === 0) return none;
|
|
27
|
+
|
|
28
|
+
const ac = new AbortController();
|
|
29
|
+
const box = doc.getElementById('di-hero-pause');
|
|
30
|
+
const live = [];
|
|
31
|
+
let warned = false;
|
|
32
|
+
const warn = (msg, err) => {
|
|
33
|
+
if (warned) return;
|
|
34
|
+
warned = true;
|
|
35
|
+
log.warn(`pkgsite-hero: ${msg}`, err ?? '');
|
|
36
|
+
};
|
|
37
|
+
// Same scheme and origin as the page. blob:https://site/x reports the page's origin, so the scheme is compared
|
|
38
|
+
// too; data: and javascript: have origin "null", and a "null" page origin (file:) matches nothing.
|
|
39
|
+
const sameOrigin = (src) => {
|
|
40
|
+
try {
|
|
41
|
+
const u = new URL(src, doc.baseURI);
|
|
42
|
+
const b = new URL(doc.baseURI);
|
|
43
|
+
return b.origin !== 'null' && u.origin === b.origin && u.protocol === b.protocol;
|
|
44
|
+
} catch { return false; }
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const set = (h, paused) => {
|
|
48
|
+
if (h.paused === paused) return;
|
|
49
|
+
try {
|
|
50
|
+
if (paused) h.api.pause(); else h.api.play();
|
|
51
|
+
} catch (err) {
|
|
52
|
+
return warn(`${h.el.dataset.heroSrc} threw from ${paused ? 'pause' : 'play'}`, err);
|
|
53
|
+
}
|
|
54
|
+
h.paused = paused;
|
|
55
|
+
h.el.dataset.heroState = paused ? 'paused' : 'playing';
|
|
56
|
+
};
|
|
57
|
+
const wanted = () => Boolean(box?.checked) || doc.visibilityState === 'hidden';
|
|
58
|
+
const sync = () => { const p = wanted(); for (const h of live) set(h, p); };
|
|
59
|
+
box?.addEventListener('change', sync, { signal: ac.signal });
|
|
60
|
+
doc.addEventListener('visibilitychange', sync, { signal: ac.signal });
|
|
61
|
+
|
|
62
|
+
const ready = Promise.all(mounts.map(async (el) => {
|
|
63
|
+
const src = el.dataset.heroSrc;
|
|
64
|
+
const poster = el.querySelector('img');
|
|
65
|
+
const ps = el.dataset.heroPoster;
|
|
66
|
+
if (poster && ps && !RASTER_DATA.test(ps) && !sameOrigin(ps)) {
|
|
67
|
+
poster.removeAttribute('src');
|
|
68
|
+
warn(`poster ${ps} is not from this site; dropped`);
|
|
69
|
+
}
|
|
70
|
+
if (!sameOrigin(src)) return warn(`${src} is not from this site; keeping the poster`);
|
|
71
|
+
let api;
|
|
72
|
+
try {
|
|
73
|
+
const mod = await importFn(new URL(src, doc.baseURI).href);
|
|
74
|
+
api = await mod.default(el, { signal: ac.signal });
|
|
75
|
+
} catch (err) {
|
|
76
|
+
return warn(`could not load ${src}; keeping the poster`, err);
|
|
77
|
+
}
|
|
78
|
+
if (!api || typeof api.pause !== 'function' || typeof api.play !== 'function') {
|
|
79
|
+
try { api?.destroy?.(); } catch { /* the poster stays either way */ }
|
|
80
|
+
return warn(`${src} must return { pause, play, destroy }; keeping the poster`);
|
|
81
|
+
}
|
|
82
|
+
if (ac.signal.aborted) { try { api.destroy?.(); } catch { /* gone */ } return; }
|
|
83
|
+
const h = { el, api, paused: false };
|
|
84
|
+
el.dataset.heroState = 'playing';
|
|
85
|
+
live.push(h);
|
|
86
|
+
if (poster) poster.hidden = true;
|
|
87
|
+
if (wanted()) set(h, true);
|
|
88
|
+
}));
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
ready,
|
|
92
|
+
destroy() {
|
|
93
|
+
ac.abort();
|
|
94
|
+
for (const h of live) { try { h.api.destroy?.(); } catch { /* already gone */ } }
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (typeof document !== 'undefined') initHeroes();
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
// @dataimago/css@0.1.0-alpha.24 dist/quarto/_extensions/dataimago/pkgsite/pkgsite.scss · sha256:18ca9b65e75a0517b19dc60ed34ea90637f34369373950da1a19ebe7444fe2be · generated from tokens; do not edit · restore: npx -p @dataimago/css@0.1.0-alpha.24 dataimago-sync
|
|
2
|
+
/*-- scss:rules --*/
|
|
3
|
+
// Package-site layer on the dataimago tokens. Colours come only from the token custom
|
|
4
|
+
// properties, so the light/dark pair switches at runtime. Josefin Sans carries headings and
|
|
5
|
+
// labels; Noto Sans, Noto Sans Mono and Noto Sans Math carry everything else.
|
|
6
|
+
|
|
7
|
+
code, kbd, pre, samp, .sourceCode { font-family: var(--font-family-mono); }
|
|
8
|
+
.math.inline, .math.display { font-family: var(--font-family-math); }
|
|
9
|
+
|
|
10
|
+
// ── Navbar: the page's own navbar; pinned or hiding, the landing hero sits at top: 0 ──
|
|
11
|
+
.navbar {
|
|
12
|
+
border-bottom: 1px solid var(--color-border-subtle);
|
|
13
|
+
.navbar-title, .navbar-nav .nav-link {
|
|
14
|
+
font-family: var(--font-family-display);
|
|
15
|
+
letter-spacing: 0.02em;
|
|
16
|
+
}
|
|
17
|
+
.navbar-title { font-weight: var(--font-weight-normal); }
|
|
18
|
+
.navbar-brand-logo img { height: 2.4rem; width: auto; }
|
|
19
|
+
.pkg-version {
|
|
20
|
+
font-family: var(--font-family-sans);
|
|
21
|
+
font-size: 0.72rem;
|
|
22
|
+
color: var(--color-content-secondary);
|
|
23
|
+
margin-left: 0.4rem;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
h1, h2, h3, h4, .title {
|
|
28
|
+
font-family: var(--font-family-display);
|
|
29
|
+
font-feature-settings: var(--font-feature-settings-display);
|
|
30
|
+
}
|
|
31
|
+
h1.title, .quarto-title h1 { font-weight: var(--font-weight-normal); letter-spacing: -0.01em; }
|
|
32
|
+
|
|
33
|
+
// ── Kicker labels ──
|
|
34
|
+
.kicker {
|
|
35
|
+
font-family: var(--font-family-display);
|
|
36
|
+
font-size: 0.8rem;
|
|
37
|
+
font-weight: var(--font-weight-semibold);
|
|
38
|
+
letter-spacing: 0.14em;
|
|
39
|
+
text-transform: uppercase;
|
|
40
|
+
color: var(--color-content-secondary);
|
|
41
|
+
border-top: 2px solid var(--color-interactive-primary);
|
|
42
|
+
padding-top: var(--spacing-sm);
|
|
43
|
+
margin: var(--spacing-3xl) 0 var(--spacing-lg);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.pkg-badges {
|
|
47
|
+
display: flex;
|
|
48
|
+
flex-wrap: wrap;
|
|
49
|
+
gap: var(--spacing-xs);
|
|
50
|
+
margin: var(--spacing-sm) 0;
|
|
51
|
+
padding: 0;
|
|
52
|
+
list-style: none;
|
|
53
|
+
img { height: 20px; width: auto; }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ── Pill links ──
|
|
57
|
+
.pill-links {
|
|
58
|
+
display: flex;
|
|
59
|
+
flex-wrap: wrap;
|
|
60
|
+
gap: var(--spacing-sm);
|
|
61
|
+
padding: 0;
|
|
62
|
+
margin: var(--spacing-md) 0;
|
|
63
|
+
list-style: none;
|
|
64
|
+
}
|
|
65
|
+
main.content .pill-links a {
|
|
66
|
+
display: inline-flex;
|
|
67
|
+
align-items: center;
|
|
68
|
+
padding: 0.35rem 0.9rem;
|
|
69
|
+
border: 1px solid var(--color-border-moderate);
|
|
70
|
+
border-radius: var(--radius-full);
|
|
71
|
+
color: var(--color-content-primary);
|
|
72
|
+
text-decoration: none !important;
|
|
73
|
+
font-size: 0.92rem;
|
|
74
|
+
transition: background-color var(--transition-quick), border-color var(--transition-quick);
|
|
75
|
+
&:hover { background-color: var(--navigation-hover-background); border-color: var(--color-interactive-primary); }
|
|
76
|
+
&:focus-visible { outline: 2px solid var(--color-interactive-focus); outline-offset: 2px; }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ── Reference index ──
|
|
80
|
+
.reference-index {
|
|
81
|
+
dl { display: grid; grid-template-columns: minmax(10rem, 18rem) 1fr; gap: var(--spacing-sm) var(--spacing-lg); }
|
|
82
|
+
dt { font-family: var(--font-family-mono); font-size: 0.92rem; }
|
|
83
|
+
dd { margin: 0; color: var(--color-content-secondary); }
|
|
84
|
+
}
|
|
85
|
+
@media (max-width: 575.98px) { .reference-index dl { grid-template-columns: 1fr; } }
|
|
86
|
+
|
|
87
|
+
// ── Reference pages and articles ──
|
|
88
|
+
main.content {
|
|
89
|
+
pre { border: 1px solid var(--color-border-subtle); border-radius: var(--radius-md); padding: var(--spacing-md); }
|
|
90
|
+
table { width: 100%; }
|
|
91
|
+
h2 { font-size: 1.5rem; margin-top: var(--spacing-2xl); }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ── Footer: the Center is credited here ──
|
|
95
|
+
.nav-footer {
|
|
96
|
+
border-top: 1px solid var(--color-border-subtle);
|
|
97
|
+
font-size: 0.9rem;
|
|
98
|
+
color: var(--color-content-secondary);
|
|
99
|
+
a { color: inherit; text-decoration: underline; text-underline-offset: 0.15em; }
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
main.content dl:not(.quarto-listing-category) {
|
|
103
|
+
dt { font-family: var(--font-family-mono); font-size: 0.92rem; margin-top: var(--spacing-md); }
|
|
104
|
+
dd { margin-left: var(--spacing-lg); }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
main.content .reference-index dl dt { margin-top: 0; }
|
|
108
|
+
main.content .reference-index dl dd { margin-left: 0; }
|
|
109
|
+
|
|
110
|
+
// ═══ The landing stage (landing.lua) ═══
|
|
111
|
+
// DOM order: pause control, intro, overview, hero, cards. Small screens keep that order as one column with the
|
|
112
|
+
// hero between intro and overview. On a large screen with motion allowed, the hero is pinned left (7fr), the
|
|
113
|
+
// intro and overview scroll beside it (3fr), and the cards panel wipes over it. No JavaScript.
|
|
114
|
+
|
|
115
|
+
.di-hero-pause-input {
|
|
116
|
+
position: absolute;
|
|
117
|
+
width: 1px;
|
|
118
|
+
height: 1px;
|
|
119
|
+
margin: -1px;
|
|
120
|
+
padding: 0;
|
|
121
|
+
overflow: hidden;
|
|
122
|
+
clip: rect(0 0 0 0);
|
|
123
|
+
white-space: nowrap;
|
|
124
|
+
border: 0;
|
|
125
|
+
}
|
|
126
|
+
.di-hero-pause { margin: var(--spacing-md) 0 0; }
|
|
127
|
+
.di-hero-pause-label {
|
|
128
|
+
display: inline-flex;
|
|
129
|
+
align-items: center;
|
|
130
|
+
box-sizing: border-box;
|
|
131
|
+
min-height: 44px;
|
|
132
|
+
padding: 0.25rem 1.1rem;
|
|
133
|
+
border: 1px solid var(--color-border-moderate);
|
|
134
|
+
border-radius: var(--radius-full);
|
|
135
|
+
background: var(--color-surface-elevated);
|
|
136
|
+
color: var(--color-content-primary);
|
|
137
|
+
font-family: var(--font-family-display);
|
|
138
|
+
font-size: 0.9rem;
|
|
139
|
+
letter-spacing: 0.04em;
|
|
140
|
+
cursor: pointer;
|
|
141
|
+
transition: border-color 120ms ease, background-color 120ms ease;
|
|
142
|
+
&:hover { border-color: var(--color-interactive-primary); }
|
|
143
|
+
.di-pause-off { display: none; }
|
|
144
|
+
}
|
|
145
|
+
.di-hero-pause-input:focus-visible ~ .di-landing .di-hero-pause-label { outline: 2px solid var(--color-interactive-focus); outline-offset: 2px; }
|
|
146
|
+
.di-hero-pause-input:checked ~ .di-landing .di-hero-pause-label {
|
|
147
|
+
.di-pause-on { display: none; }
|
|
148
|
+
.di-pause-off { display: inline; }
|
|
149
|
+
}
|
|
150
|
+
@media (prefers-reduced-motion: reduce) { .di-hero-pause { display: none; } }
|
|
151
|
+
|
|
152
|
+
#di-hero-pause:checked ~ .di-landing .di-landing-hero * { animation-play-state: paused; }
|
|
153
|
+
|
|
154
|
+
/* The landing filter sets toc: false; this is the belt for a TOC or margin column that still appears. */
|
|
155
|
+
body:has(.di-landing) #TOC,
|
|
156
|
+
body:has(.di-landing) #quarto-margin-sidebar { display: none; }
|
|
157
|
+
|
|
158
|
+
.di-landing {
|
|
159
|
+
display: flex;
|
|
160
|
+
flex-direction: column;
|
|
161
|
+
gap: var(--spacing-xl);
|
|
162
|
+
margin: 0;
|
|
163
|
+
}
|
|
164
|
+
.di-landing-intro { order: 1; padding: var(--spacing-2xl) var(--spacing-lg) 0; }
|
|
165
|
+
.di-landing-hero { order: 2; max-height: 50vh; overflow: hidden; }
|
|
166
|
+
.di-landing-overview { order: 3; padding-inline: var(--spacing-lg); }
|
|
167
|
+
.di-landing-cards { order: 4; }
|
|
168
|
+
|
|
169
|
+
.di-landing-hero svg, .di-landing-hero img { display: block; width: 100%; height: 100%; max-height: 50vh; object-fit: contain; }
|
|
170
|
+
.di-hero-mount { width: 100%; height: 100%; }
|
|
171
|
+
|
|
172
|
+
.di-landing-intro {
|
|
173
|
+
h1.di-landing-name {
|
|
174
|
+
font-family: var(--font-family-display);
|
|
175
|
+
font-weight: var(--font-weight-normal);
|
|
176
|
+
font-size: clamp(2.4rem, 5vw, 3.6rem);
|
|
177
|
+
line-height: var(--font-lineHeight-tight);
|
|
178
|
+
margin: 0;
|
|
179
|
+
}
|
|
180
|
+
.di-landing-title {
|
|
181
|
+
color: var(--color-content-secondary);
|
|
182
|
+
font-size: 1.1rem;
|
|
183
|
+
margin: var(--spacing-xs) 0 var(--spacing-md);
|
|
184
|
+
max-width: 42rem;
|
|
185
|
+
}
|
|
186
|
+
.di-landing-version { font-size: 0.75rem; color: var(--color-content-secondary); margin: 0; }
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.di-landing-cards {
|
|
190
|
+
display: grid;
|
|
191
|
+
grid-template-columns: 1fr;
|
|
192
|
+
gap: var(--spacing-md);
|
|
193
|
+
padding: var(--spacing-xl) var(--spacing-lg);
|
|
194
|
+
background: var(--color-surface-page);
|
|
195
|
+
}
|
|
196
|
+
@media (min-width: 640px) { .di-landing-cards { grid-template-columns: repeat(2, 1fr); } }
|
|
197
|
+
|
|
198
|
+
a.di-card {
|
|
199
|
+
display: flex;
|
|
200
|
+
flex-direction: column;
|
|
201
|
+
gap: var(--spacing-xs);
|
|
202
|
+
padding: var(--spacing-lg);
|
|
203
|
+
border: 1px solid var(--color-border-card);
|
|
204
|
+
border-radius: var(--radius-md);
|
|
205
|
+
background: var(--color-surface-elevated);
|
|
206
|
+
color: var(--color-content-primary);
|
|
207
|
+
text-decoration: none !important;
|
|
208
|
+
transition: border-color 120ms ease, background-color 120ms ease;
|
|
209
|
+
&:hover { border-color: var(--color-interactive-primary); background-color: var(--navigation-hover-background); }
|
|
210
|
+
&:focus-visible { outline: 2px solid var(--color-interactive-focus); outline-offset: 2px; }
|
|
211
|
+
.di-card-title { font-family: var(--font-family-display); font-size: 1.15rem; font-weight: var(--font-weight-semibold); }
|
|
212
|
+
.di-card-desc { color: var(--color-content-secondary); font-size: 0.92rem; }
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// A pinned navbar is fixed over the page, so the pinned hero starts below it. Quarto's headroom script puts a
|
|
216
|
+
// headroom--* class on the header (a hiding navbar); a pinned navbar never gets one, so CSS alone tells them
|
|
217
|
+
// apart. On a landing page the navbar is given a floor of 4rem (Quarto's own body padding-top is 64px) so the
|
|
218
|
+
// offset is exact; every other page keeps Quarto's natural navbar height.
|
|
219
|
+
|
|
220
|
+
// The wipe: only with motion allowed and room for a pinned stage.
|
|
221
|
+
@media (prefers-reduced-motion: no-preference) and (min-width: 1024px) and (min-height: 780px) {
|
|
222
|
+
.di-landing {
|
|
223
|
+
display: grid;
|
|
224
|
+
grid-template-columns: 7fr 3fr;
|
|
225
|
+
grid-template-rows: auto auto auto;
|
|
226
|
+
gap: 0;
|
|
227
|
+
}
|
|
228
|
+
.di-landing-hero {
|
|
229
|
+
grid-column: 1;
|
|
230
|
+
grid-row: 1 / 4;
|
|
231
|
+
position: sticky;
|
|
232
|
+
top: 0;
|
|
233
|
+
height: 100dvh;
|
|
234
|
+
max-height: none;
|
|
235
|
+
align-self: start;
|
|
236
|
+
}
|
|
237
|
+
body:has(.di-landing) #quarto-header .navbar { min-height: 4rem; }
|
|
238
|
+
// Pinned navbar: start below it (a new custom property would extend the frozen CSS-variable contract).
|
|
239
|
+
body.nav-fixed:has(#quarto-header:not([class*="headroom--"])) .di-landing-hero {
|
|
240
|
+
top: 4rem;
|
|
241
|
+
height: calc(100dvh - 4rem);
|
|
242
|
+
}
|
|
243
|
+
.di-landing-hero svg, .di-landing-hero img { max-height: none; }
|
|
244
|
+
.di-landing-intro {
|
|
245
|
+
grid-column: 2;
|
|
246
|
+
grid-row: 1;
|
|
247
|
+
min-height: 100dvh;
|
|
248
|
+
padding: 0 var(--spacing-xl);
|
|
249
|
+
display: flex;
|
|
250
|
+
flex-direction: column;
|
|
251
|
+
justify-content: center;
|
|
252
|
+
}
|
|
253
|
+
.di-landing-overview { grid-column: 2; grid-row: 2; padding: var(--spacing-xl); }
|
|
254
|
+
.di-landing-cards {
|
|
255
|
+
grid-column: 1 / -1;
|
|
256
|
+
grid-row: 3;
|
|
257
|
+
position: relative;
|
|
258
|
+
z-index: 1;
|
|
259
|
+
min-height: 100dvh;
|
|
260
|
+
align-content: center;
|
|
261
|
+
padding: var(--spacing-3xl) var(--spacing-xl);
|
|
262
|
+
background: var(--color-surface-page);
|
|
263
|
+
box-shadow: var(--shadow-prominent);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// AI-composed label (provenance.lua): quiet surface, secondary text, a left rule. The draft rule is stronger and
|
|
268
|
+
// uses the accent token. Pairs: content-secondary on surface-elevated is a contrast-check role in both modes;
|
|
269
|
+
// the rule is non-text and clears 3:1 against that surface (interactive-accent, light and dark).
|
|
270
|
+
.di-provenance {
|
|
271
|
+
margin: 0 0 var(--spacing-lg);
|
|
272
|
+
padding: var(--spacing-sm) var(--spacing-md);
|
|
273
|
+
border-left: 3px solid var(--color-border-moderate);
|
|
274
|
+
background: var(--color-surface-elevated);
|
|
275
|
+
color: var(--color-content-secondary);
|
|
276
|
+
font-size: 0.875rem;
|
|
277
|
+
line-height: 1.5;
|
|
278
|
+
p { margin: 0; }
|
|
279
|
+
a { color: inherit; text-decoration: underline; }
|
|
280
|
+
&.di-provenance-draft { border-left-width: 6px; border-left-color: var(--color-interactive-accent); }
|
|
281
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @dataimago/css@0.1.0-alpha.
|
|
1
|
+
// @dataimago/css@0.1.0-alpha.24 dist/quarto/components.scss · sha256:a2b35c50f5dc36aa4cf337e7ff5ea4ec758890c6910776f5a3e1deb31ad44dd4 · generated from tokens; do not edit · restore: npx -p @dataimago/css@0.1.0-alpha.24 dataimago-sync
|
|
2
2
|
/*-- scss:rules --*/
|
|
3
3
|
// dataimago components (.di-*) — generated from src/styles/components/.
|
|
4
4
|
.di-figure-box {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @dataimago/css@0.1.0-alpha.
|
|
1
|
+
// @dataimago/css@0.1.0-alpha.24 dist/quarto/dataimago-dark.scss · sha256:f857888d73961ee714eea97a162915d1f3c28fa740fc88bf3a68e8728594d274 · generated from tokens; do not edit · restore: npx -p @dataimago/css@0.1.0-alpha.24 dataimago-sync
|
|
2
2
|
// dataimago dark theme for Quarto 1.8 — generated from the design tokens (tools/formats/quarto.mjs).
|
|
3
3
|
// Bootstrap Sass variables are compile-time sRGB; runtime colours come from the custom properties below.
|
|
4
4
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @dataimago/css@0.1.0-alpha.
|
|
1
|
+
// @dataimago/css@0.1.0-alpha.24 dist/quarto/dataimago-light.scss · sha256:e456b3e8cfaa03df7fe5ac29bb6e8288454ee5daab29ff6470c45a12ff8d3f32 · generated from tokens; do not edit · restore: npx -p @dataimago/css@0.1.0-alpha.24 dataimago-sync
|
|
2
2
|
// dataimago light theme for Quarto 1.8 — generated from the design tokens (tools/formats/quarto.mjs).
|
|
3
3
|
// Bootstrap Sass variables are compile-time sRGB; runtime colours come from the custom properties below.
|
|
4
4
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- @dataimago/css@0.1.0-alpha.
|
|
1
|
+
<!-- @dataimago/css@0.1.0-alpha.24 dist/templates/KNOWLEDGE-explorer.md · sha256:907c08638ed56813d453227759e29de08350465885d11f0efe94ec7ba8c42a50 · generated from tokens; do not edit · restore: npx -p @dataimago/css@0.1.0-alpha.24 dataimago-sync -->
|
|
2
2
|
# {{PROJECT_NAME}} — Knowledge Base Operating Manual
|
|
3
3
|
|
|
4
4
|
**Domain:** {{DOMAIN_NAME}}
|