explico 0.3.0 → 0.4.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/CHANGELOG.md +72 -1
- package/README.md +21 -2
- package/dist/compiler/astro.d.ts +30 -0
- package/dist/compiler/astro.js +79 -0
- package/dist/compiler/cli.js +5 -9
- package/dist/compiler/course-config.d.ts +29 -4
- package/dist/compiler/course-config.js +12 -0
- package/dist/compiler/lesson-derivation.d.ts +1 -1
- package/dist/compiler/lesson-derivation.js +2 -2
- package/dist/compiler/manifest.d.ts +25 -4
- package/dist/compiler/manifest.js +59 -27
- package/dist/reference/equations-validate.d.ts +8 -3
- package/dist/reference/equations-validate.js +1 -1
- package/dist/reference/equations.d.mts +58 -22
- package/dist/reference/equations.mjs +75 -26
- package/dist/reference/keyed-float-validate.d.ts +9 -3
- package/dist/reference/keyed-float-validate.js +1 -1
- package/dist/reference/keyed-jsx-floats.d.mts +18 -10
- package/dist/reference/keyed-jsx-floats.mjs +42 -18
- package/dist/reference/remark-notation.d.mts +54 -5
- package/dist/reference/remark-notation.mjs +161 -17
- package/docs/authoring/README.md +4 -0
- package/docs/authoring/migrating.md +62 -0
- package/docs/authoring/notation.md +8 -7
- package/docs/authoring/structure.md +139 -0
- package/package.json +1 -1
- package/src/compiler/astro.ts +127 -0
- package/src/compiler/cli.ts +5 -9
- package/src/compiler/course-config.ts +39 -3
- package/src/compiler/lesson-derivation.ts +2 -2
- package/src/compiler/manifest.ts +108 -36
- package/src/components/notation/NotationLayer.astro +11 -0
- package/src/components/starlight/LayoutSidebar.astro +62 -1
- package/src/components/starlight/LessonFooter.astro +11 -0
- package/src/components/table/Table.astro +3 -2
- package/src/reference/equations-validate.ts +6 -4
- package/src/reference/equations.mjs +75 -26
- package/src/reference/keyed-float-validate.ts +7 -4
- package/src/reference/keyed-jsx-floats.mjs +42 -18
- package/src/reference/remark-notation.mjs +161 -17
- package/src/styles/global.css +129 -0
|
@@ -22,7 +22,11 @@
|
|
|
22
22
|
* `equations.test.ts` holds equations to.
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
resolveNumbering,
|
|
27
|
+
stripFencedCode,
|
|
28
|
+
stripJsxComponents,
|
|
29
|
+
} from './equations.mjs';
|
|
26
30
|
|
|
27
31
|
/** Same shape as `EQ_KEY_PATTERN` — the notation-key grammar without dots. */
|
|
28
32
|
export const KEYED_FLOAT_KEY_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
@@ -33,15 +37,22 @@ function floatNumber(section, index) {
|
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
/**
|
|
36
|
-
* The visible number for one float. With `
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
+
* The visible number for one float. With a `prefix` (the part's ordinal) the
|
|
41
|
+
* scheme is `‹part›.‹offset + indexInLesson›` and `##` headings are ignored;
|
|
42
|
+
* without it, the legacy `##`-section scheme. Mirrors `displayEquationNumber`
|
|
43
|
+
* in `equations.mjs`. Each kind of float counts on its own — a `<Table>` never
|
|
44
|
+
* advances the `<Figure>` sequence — so `offset` is per kind too.
|
|
40
45
|
*/
|
|
41
|
-
function keyedFloatNumber({
|
|
42
|
-
|
|
46
|
+
function keyedFloatNumber({
|
|
47
|
+
prefix,
|
|
48
|
+
offset = 0,
|
|
49
|
+
section,
|
|
50
|
+
indexInSection,
|
|
51
|
+
indexInLesson,
|
|
52
|
+
}) {
|
|
53
|
+
return prefix == null
|
|
43
54
|
? floatNumber(section, indexInSection)
|
|
44
|
-
: `${
|
|
55
|
+
: `${prefix}.${offset + indexInLesson}`;
|
|
45
56
|
}
|
|
46
57
|
|
|
47
58
|
/** The anchor id for a float, e.g. `{prefix:'tab', key:'foo'}` → `tab-foo`. */
|
|
@@ -71,13 +82,15 @@ function readIdAttribute(attributesSource) {
|
|
|
71
82
|
/**
|
|
72
83
|
* The raw-text equivalent of `collectKeyedJsxFloats`, mirroring
|
|
73
84
|
* `scanEquationLabels`: every top-level `<TagName …>` in appearance order.
|
|
74
|
-
* With `
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
85
|
+
* With `numbering` (`{ prefix, offset }` — the part's ordinal and the count of
|
|
86
|
+
* this kind of float in earlier lessons of that part) the number is
|
|
87
|
+
* `‹part›.‹offset + n›`, `n` sequential over the whole lesson; without it, `##`
|
|
88
|
+
* headings advance a section counter. Other JSX components' bodies are blanked
|
|
89
|
+
* first so a `<Table>` nested inside e.g. `<CompactExample>` (and any `##`-like
|
|
90
|
+
* text inside an unrelated component) is never counted.
|
|
79
91
|
*/
|
|
80
|
-
export function scanKeyedJsxFloats(markdown, { tagName,
|
|
92
|
+
export function scanKeyedJsxFloats(markdown, { tagName, numbering }) {
|
|
93
|
+
const { prefix, offset } = resolveNumbering(numbering);
|
|
81
94
|
const text = stripJsxComponents(stripFencedCode(markdown), tagName);
|
|
82
95
|
const tokens = [];
|
|
83
96
|
const heading = /^[ \t]{0,3}##[ \t]+\S/gm;
|
|
@@ -106,7 +119,8 @@ export function scanKeyedJsxFloats(markdown, { tagName, chapter }) {
|
|
|
106
119
|
labels.push({
|
|
107
120
|
key: token.key,
|
|
108
121
|
number: keyedFloatNumber({
|
|
109
|
-
|
|
122
|
+
prefix,
|
|
123
|
+
offset,
|
|
110
124
|
section,
|
|
111
125
|
indexInSection,
|
|
112
126
|
indexInLesson,
|
|
@@ -118,14 +132,23 @@ export function scanKeyedJsxFloats(markdown, { tagName, chapter }) {
|
|
|
118
132
|
return labels;
|
|
119
133
|
}
|
|
120
134
|
|
|
135
|
+
/**
|
|
136
|
+
* How many floats of this kind a lesson body holds — the input to the next
|
|
137
|
+
* lesson's `offset` within the same part.
|
|
138
|
+
*/
|
|
139
|
+
export function countKeyedJsxFloats(markdown, { tagName }) {
|
|
140
|
+
return scanKeyedJsxFloats(markdown, { tagName }).length;
|
|
141
|
+
}
|
|
142
|
+
|
|
121
143
|
/**
|
|
122
144
|
* The mdast equivalent of `scanKeyedJsxFloats`: walk the tree's *top-level*
|
|
123
145
|
* children in document order, advancing the section counter on every
|
|
124
146
|
* depth-2 heading and emitting a record for every `mdxJsxFlowElement` named
|
|
125
|
-
* `tagName` — keyed or not, exactly like `collectDisplayEquations`. `
|
|
147
|
+
* `tagName` — keyed or not, exactly like `collectDisplayEquations`. `numbering`
|
|
126
148
|
* is threaded the same way as in `scanKeyedJsxFloats`.
|
|
127
149
|
*/
|
|
128
|
-
export function collectKeyedJsxFloats(tree, { tagName,
|
|
150
|
+
export function collectKeyedJsxFloats(tree, { tagName, numbering }) {
|
|
151
|
+
const { prefix, offset } = resolveNumbering(numbering);
|
|
129
152
|
const floats = [];
|
|
130
153
|
let section = 0;
|
|
131
154
|
let indexInSection = 0;
|
|
@@ -151,7 +174,8 @@ export function collectKeyedJsxFloats(tree, { tagName, chapter }) {
|
|
|
151
174
|
floats.push({
|
|
152
175
|
key: key ?? null,
|
|
153
176
|
number: keyedFloatNumber({
|
|
154
|
-
|
|
177
|
+
prefix,
|
|
178
|
+
offset,
|
|
155
179
|
section,
|
|
156
180
|
indexInSection,
|
|
157
181
|
indexInLesson,
|
|
@@ -671,26 +671,146 @@ function inspectExplainCalls(value, resolve, file, node, references) {
|
|
|
671
671
|
}
|
|
672
672
|
}
|
|
673
673
|
|
|
674
|
+
/**
|
|
675
|
+
* The numbering inputs a lesson page needs, read off one manifest.
|
|
676
|
+
*
|
|
677
|
+
* Every number on a page comes from two places that MUST agree: the page's own
|
|
678
|
+
* counters (`numbering`) and the manifest tables a cross-page `[[slug#eq-key]]`
|
|
679
|
+
* is resolved against (`equations` / `tables` / `figures`). Deriving all four
|
|
680
|
+
* from the same manifest in one step is what stops a course config from
|
|
681
|
+
* supplying one without the other — the omission that produced page-local
|
|
682
|
+
* `(2.2)` and cross-page `(4.5)` for the same equation.
|
|
683
|
+
*
|
|
684
|
+
* `numbering` maps a lesson slug to `{ part, equations, tables, figures }`:
|
|
685
|
+
* the part's ordinal (the number before the dot) and, per kind of float, how
|
|
686
|
+
* many of them the earlier lessons of that part already used.
|
|
687
|
+
*/
|
|
688
|
+
export function numberingFromManifest(manifest) {
|
|
689
|
+
if (!manifest || typeof manifest !== 'object') return {};
|
|
690
|
+
return {
|
|
691
|
+
equations: manifest.equations?.numbersBySlug ?? {},
|
|
692
|
+
tables: manifest.tables?.numbersBySlug ?? {},
|
|
693
|
+
figures: manifest.figures?.numbersBySlug ?? {},
|
|
694
|
+
numbering: Object.fromEntries(
|
|
695
|
+
(manifest.lessons ?? []).map((lesson) => [
|
|
696
|
+
lesson.slug,
|
|
697
|
+
{
|
|
698
|
+
part: lesson.part,
|
|
699
|
+
equations: lesson.numbering?.equations ?? 0,
|
|
700
|
+
tables: lesson.numbering?.tables ?? 0,
|
|
701
|
+
figures: lesson.numbering?.figures ?? 0,
|
|
702
|
+
},
|
|
703
|
+
]),
|
|
704
|
+
),
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* `{ prefix, offset }` for one kind of float on one page, from whichever
|
|
710
|
+
* numbering option the caller supplied. `numbering` is the current form;
|
|
711
|
+
* `chapters` is the superseded per-lesson form (prefix = the lesson's ordinal,
|
|
712
|
+
* no offset) and is still read so an unmigrated config keeps working.
|
|
713
|
+
*/
|
|
714
|
+
function pageNumbering(options, slug, kind) {
|
|
715
|
+
if (!slug) return undefined;
|
|
716
|
+
const placement = options.numbering?.[slug];
|
|
717
|
+
if (placement?.part) {
|
|
718
|
+
return { prefix: placement.part, offset: placement[kind] ?? 0 };
|
|
719
|
+
}
|
|
720
|
+
const chapter = options.chapters?.[slug];
|
|
721
|
+
return chapter ? { prefix: chapter, offset: 0 } : undefined;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* Fail when the number this page just computed for a keyed float disagrees
|
|
726
|
+
* with the number the manifest publishes for the same key (WERN — a reference
|
|
727
|
+
* that shows a different number than its target). The two are produced by the
|
|
728
|
+
* same algorithm from the same content, so any disagreement means the render
|
|
729
|
+
* path and the manifest were fed different inputs; in practice, an
|
|
730
|
+
* `astro.config.mjs` that passes the manifest number tables but not
|
|
731
|
+
* `numbering`, which silently drops the page back to the legacy `##`-section
|
|
732
|
+
* scheme.
|
|
733
|
+
*/
|
|
734
|
+
function assertManifestNumberParity({
|
|
735
|
+
file,
|
|
736
|
+
slug,
|
|
737
|
+
pageLabels,
|
|
738
|
+
crossPage,
|
|
739
|
+
placement,
|
|
740
|
+
noun,
|
|
741
|
+
syntax,
|
|
742
|
+
}) {
|
|
743
|
+
// No entry for this slug at all means the caller supplied no manifest table
|
|
744
|
+
// (hand-built test trees, non-lesson pages) — nothing to cross-check.
|
|
745
|
+
const published = crossPage?.[slug];
|
|
746
|
+
if (!published) return;
|
|
747
|
+
|
|
748
|
+
for (const [key, number] of pageLabels) {
|
|
749
|
+
const expected = published[key];
|
|
750
|
+
if (expected === undefined) {
|
|
751
|
+
fail(
|
|
752
|
+
file,
|
|
753
|
+
`Stale Explico manifest for ${slug}: this page numbers ${noun} ` +
|
|
754
|
+
`${syntax(key)} as (${number}), but the manifest lists no such ` +
|
|
755
|
+
`${noun} for the lesson. Recompile the manifest before rendering.`,
|
|
756
|
+
);
|
|
757
|
+
}
|
|
758
|
+
if (expected !== number) {
|
|
759
|
+
fail(
|
|
760
|
+
file,
|
|
761
|
+
`Inconsistent ${noun} numbering for ${syntax(key)} in ${slug}: this ` +
|
|
762
|
+
`page renders it as (${number}) while a cross-page reference to it ` +
|
|
763
|
+
`resolves to (${expected}). ` +
|
|
764
|
+
(placement === undefined
|
|
765
|
+
? 'The remarkNotation options carry the manifest number tables ' +
|
|
766
|
+
'but no `numbering` entry for this lesson, so the page fell ' +
|
|
767
|
+
'back to the legacy `##`-section scheme. Pass `manifest` to ' +
|
|
768
|
+
'remarkNotation so both sides use the part scheme.'
|
|
769
|
+
: 'The page counters and the manifest disagree; recompile the ' +
|
|
770
|
+
'manifest from the current content.'),
|
|
771
|
+
);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
|
|
674
776
|
/**
|
|
675
777
|
* @param {{
|
|
676
778
|
* definitions?: Map<string, object> | readonly object[] | Record<string, object> | (() => Map<string, object> | readonly object[] | Record<string, object>),
|
|
677
779
|
* resolve?: (key: string, context: object) => object | undefined,
|
|
678
780
|
* unknown?: 'error' | 'warn' | 'ignore',
|
|
679
781
|
* base?: string,
|
|
782
|
+
* manifest?: object,
|
|
680
783
|
* equations?: Record<string, Record<string, string>>,
|
|
681
784
|
* tables?: Record<string, Record<string, string>>,
|
|
682
785
|
* figures?: Record<string, Record<string, string>>,
|
|
786
|
+
* numbering?: Record<string, { part: number, equations: number, tables: number, figures: number }>,
|
|
683
787
|
* chapters?: Record<string, number>,
|
|
684
788
|
* }} [options]
|
|
685
789
|
*
|
|
686
|
-
* `
|
|
687
|
-
*
|
|
688
|
-
*
|
|
689
|
-
*
|
|
690
|
-
*
|
|
790
|
+
* `manifest` is the preferred wiring: pass the compiled manifest and the four
|
|
791
|
+
* numbering inputs below are read off it together, so they cannot drift apart.
|
|
792
|
+
* They stay available individually for hand-built trees, and an explicitly
|
|
793
|
+
* supplied one still wins over the manifest.
|
|
794
|
+
*
|
|
795
|
+
* `numbering` maps a lesson slug to its **part** (the number before the dot)
|
|
796
|
+
* and, per kind of float, how many the earlier lessons of that part already
|
|
797
|
+
* used. Every display equation / `<Table>` / `<Figure>` on the page is then
|
|
798
|
+
* numbered `‹part›.‹offset + n›`, with `##` and `###` headings ignored; with no
|
|
799
|
+
* entry the legacy `##`-section scheme applies. It must agree with
|
|
800
|
+
* `manifest.lessons[]`, which is what feeds `equations`/`tables`/`figures`
|
|
801
|
+
* above — supplying the tables without `numbering` numbers the same equation
|
|
802
|
+
* twice, and `assertManifestNumberParity` turns that into a build error rather
|
|
803
|
+
* than a page whose references disagree with their targets.
|
|
804
|
+
*
|
|
805
|
+
* `chapters` is the superseded per-lesson form (slug → the lesson's course
|
|
806
|
+
* ordinal, no running offset). Still read when `numbering` has no entry, so a
|
|
807
|
+
* config written against the old option keeps building.
|
|
691
808
|
*/
|
|
692
809
|
export default function remarkNotation(options = {}) {
|
|
693
810
|
const unknown = options.unknown ?? 'error';
|
|
811
|
+
// The manifest supplies every numbering input at once; an explicit option
|
|
812
|
+
// still overrides it, so existing per-field wiring keeps working.
|
|
813
|
+
const numbering = { ...numberingFromManifest(options.manifest), ...options };
|
|
694
814
|
|
|
695
815
|
return function transform(tree, file) {
|
|
696
816
|
const suppliedDefinitions =
|
|
@@ -703,10 +823,10 @@ export default function remarkNotation(options = {}) {
|
|
|
703
823
|
);
|
|
704
824
|
const localDefinitions = localDefinitionsFromFile(file);
|
|
705
825
|
const lessonId = lessonIdFrom(file);
|
|
706
|
-
|
|
707
|
-
//
|
|
708
|
-
|
|
709
|
-
|
|
826
|
+
const lessonSlug = lessonId ? lessonId.replace(/\./g, '/') : undefined;
|
|
827
|
+
// `{ prefix, offset }` per kind of float — see the `numbering` option.
|
|
828
|
+
// Missing ⇒ the legacy `##`-section numbering.
|
|
829
|
+
const equationNumbering = pageNumbering(numbering, lessonSlug, 'equations');
|
|
710
830
|
const registryFile = isNotationRegistryFile(file);
|
|
711
831
|
const mathScope = pageGlyphScope(
|
|
712
832
|
localDefinitions,
|
|
@@ -718,8 +838,9 @@ export default function remarkNotation(options = {}) {
|
|
|
718
838
|
|
|
719
839
|
// Equation identity (D7): assign every `\label{eq:…}` its number before any
|
|
720
840
|
// math node is rewritten, so a `[[eq-key]]` earlier in the page still
|
|
721
|
-
// resolves. `
|
|
722
|
-
|
|
841
|
+
// resolves. `equationNumbering` picks the `‹part›.‹n›` vs `##`-section
|
|
842
|
+
// scheme.
|
|
843
|
+
const equationLabels = collectEquationLabels(tree, equationNumbering);
|
|
723
844
|
const pageLabels = new Map();
|
|
724
845
|
for (const label of equationLabels) {
|
|
725
846
|
if (pageLabels.has(label.key)) {
|
|
@@ -734,14 +855,25 @@ export default function remarkNotation(options = {}) {
|
|
|
734
855
|
const eqCtx = {
|
|
735
856
|
base: options.base ?? '',
|
|
736
857
|
pageLabels,
|
|
737
|
-
crossPage:
|
|
858
|
+
crossPage: numbering.equations ?? {},
|
|
738
859
|
};
|
|
860
|
+
// WERN guard: what this page prints next to the equation must be what a
|
|
861
|
+
// reference from another page prints for it.
|
|
862
|
+
assertManifestNumberParity({
|
|
863
|
+
file,
|
|
864
|
+
slug: lessonSlug,
|
|
865
|
+
pageLabels,
|
|
866
|
+
crossPage: eqCtx.crossPage,
|
|
867
|
+
placement: equationNumbering,
|
|
868
|
+
noun: 'equation',
|
|
869
|
+
syntax: (key) => `eq:${key}`,
|
|
870
|
+
});
|
|
739
871
|
|
|
740
872
|
// Number and anchor every display equation on a lesson page, keyed or
|
|
741
873
|
// not (F3). Non-lesson pages (notation registry, stray docs) keep the
|
|
742
874
|
// keyed-only behaviour so their body math is left untouched.
|
|
743
875
|
const displayEquations = lessonId
|
|
744
|
-
? collectDisplayEquations(tree,
|
|
876
|
+
? collectDisplayEquations(tree, equationNumbering)
|
|
745
877
|
: [];
|
|
746
878
|
const equationByNode = new Map(
|
|
747
879
|
displayEquations.map((equation) => [equation.node, equation]),
|
|
@@ -750,9 +882,10 @@ export default function remarkNotation(options = {}) {
|
|
|
750
882
|
// Keyed floats (F-tables-figures): every `<Table>`/`<Figure>` on a lesson
|
|
751
883
|
// page is numbered and anchored, keyed or not — same rule as equations,
|
|
752
884
|
// via the shared engine in `keyed-jsx-floats.mjs`.
|
|
753
|
-
const makeFloatCtx = (tagName, crossPage) => {
|
|
885
|
+
const makeFloatCtx = (tagName, crossPage, kind) => {
|
|
886
|
+
const floatNumbering = pageNumbering(numbering, lessonSlug, kind);
|
|
754
887
|
const floats = lessonId
|
|
755
|
-
? collectKeyedJsxFloats(tree, { tagName,
|
|
888
|
+
? collectKeyedJsxFloats(tree, { tagName, numbering: floatNumbering })
|
|
756
889
|
: [];
|
|
757
890
|
const pageFloatLabels = new Map();
|
|
758
891
|
for (const float of floats) {
|
|
@@ -766,6 +899,15 @@ export default function remarkNotation(options = {}) {
|
|
|
766
899
|
}
|
|
767
900
|
pageFloatLabels.set(float.key, float.number);
|
|
768
901
|
}
|
|
902
|
+
assertManifestNumberParity({
|
|
903
|
+
file,
|
|
904
|
+
slug: lessonSlug,
|
|
905
|
+
pageLabels: pageFloatLabels,
|
|
906
|
+
crossPage: crossPage ?? {},
|
|
907
|
+
placement: floatNumbering,
|
|
908
|
+
noun: tagName,
|
|
909
|
+
syntax: (key) => `<${tagName} id="${key}">`,
|
|
910
|
+
});
|
|
769
911
|
return {
|
|
770
912
|
ctx: {
|
|
771
913
|
base: options.base ?? '',
|
|
@@ -777,11 +919,13 @@ export default function remarkNotation(options = {}) {
|
|
|
777
919
|
};
|
|
778
920
|
const { ctx: tableCtx, byNode: tableByNode } = makeFloatCtx(
|
|
779
921
|
'Table',
|
|
780
|
-
|
|
922
|
+
numbering.tables,
|
|
923
|
+
'tables',
|
|
781
924
|
);
|
|
782
925
|
const { ctx: figureCtx, byNode: figureByNode } = makeFloatCtx(
|
|
783
926
|
'Figure',
|
|
784
|
-
|
|
927
|
+
numbering.figures,
|
|
928
|
+
'figures',
|
|
785
929
|
);
|
|
786
930
|
|
|
787
931
|
const resolve = (key, kind, node) => {
|
package/src/styles/global.css
CHANGED
|
@@ -448,6 +448,135 @@
|
|
|
448
448
|
min-height: 100%;
|
|
449
449
|
}
|
|
450
450
|
|
|
451
|
+
/* A compact chapter remains useful as a row of lesson icons. The
|
|
452
|
+
link text doubles as a pointer/keyboard tooltip; opening the chapter via
|
|
453
|
+
its summary restores Starlight's normal, fully labelled list. */
|
|
454
|
+
.layout-compact-sidebar
|
|
455
|
+
details[data-compact]
|
|
456
|
+
> ul:not(.top-level) {
|
|
457
|
+
display: flex;
|
|
458
|
+
flex-wrap: wrap;
|
|
459
|
+
gap: 0.35rem;
|
|
460
|
+
margin: 0.2rem 0 0.35rem;
|
|
461
|
+
padding-inline-start: 0.5rem;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
.layout-compact-sidebar
|
|
465
|
+
details[data-compact]
|
|
466
|
+
> ul:not(.top-level)
|
|
467
|
+
> li {
|
|
468
|
+
position: relative;
|
|
469
|
+
margin-inline-start: 0;
|
|
470
|
+
border-inline-start: 0;
|
|
471
|
+
padding-inline-start: 0;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.layout-compact-sidebar
|
|
475
|
+
details[data-compact]
|
|
476
|
+
> ul:not(.top-level)
|
|
477
|
+
> li
|
|
478
|
+
> a {
|
|
479
|
+
display: grid;
|
|
480
|
+
position: relative;
|
|
481
|
+
place-items: center;
|
|
482
|
+
width: 1.75rem;
|
|
483
|
+
height: 1.75rem;
|
|
484
|
+
border: 1px solid var(--sl-color-gray-5);
|
|
485
|
+
border-radius: 0.4rem;
|
|
486
|
+
padding: 0;
|
|
487
|
+
color: var(--sl-color-gray-3);
|
|
488
|
+
background: var(--sl-color-bg-sidebar);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
.layout-compact-sidebar
|
|
492
|
+
details[data-compact]
|
|
493
|
+
> ul:not(.top-level)
|
|
494
|
+
> li
|
|
495
|
+
> a::before {
|
|
496
|
+
content: '';
|
|
497
|
+
width: 0.65rem;
|
|
498
|
+
height: 0.78rem;
|
|
499
|
+
border: 1.5px solid currentColor;
|
|
500
|
+
border-radius: 0.13rem;
|
|
501
|
+
box-shadow: 0.18rem 0.18rem 0 -0.1rem var(--sl-color-bg-sidebar),
|
|
502
|
+
0.18rem 0.18rem 0 0 currentColor;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.layout-compact-sidebar
|
|
506
|
+
details[data-compact]
|
|
507
|
+
> ul:not(.top-level)
|
|
508
|
+
> li
|
|
509
|
+
> a:hover,
|
|
510
|
+
.layout-compact-sidebar
|
|
511
|
+
details[data-compact]
|
|
512
|
+
> ul:not(.top-level)
|
|
513
|
+
> li
|
|
514
|
+
> a:focus-visible {
|
|
515
|
+
border-color: var(--sl-color-accent);
|
|
516
|
+
color: var(--sl-color-text-accent);
|
|
517
|
+
background: var(--sl-color-accent-low);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
.layout-compact-sidebar
|
|
521
|
+
details[data-compact]
|
|
522
|
+
> ul:not(.top-level)
|
|
523
|
+
> li
|
|
524
|
+
> a[aria-current='page'] {
|
|
525
|
+
color: var(--sl-color-text-invert);
|
|
526
|
+
background: var(--sl-color-text-accent);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
.layout-compact-sidebar
|
|
530
|
+
details[data-compact]
|
|
531
|
+
> ul:not(.top-level)
|
|
532
|
+
> li
|
|
533
|
+
> a
|
|
534
|
+
> span {
|
|
535
|
+
position: absolute;
|
|
536
|
+
z-index: 2;
|
|
537
|
+
inset-block-start: 50%;
|
|
538
|
+
inset-inline-start: calc(100% + 0.45rem);
|
|
539
|
+
width: max-content;
|
|
540
|
+
max-width: 12rem;
|
|
541
|
+
border: 1px solid var(--sl-color-gray-5);
|
|
542
|
+
border-radius: 0.35rem;
|
|
543
|
+
padding: 0.3rem 0.5rem;
|
|
544
|
+
color: var(--sl-color-white);
|
|
545
|
+
background: var(--sl-color-bg-nav);
|
|
546
|
+
box-shadow: var(--sl-shadow-md);
|
|
547
|
+
font-size: var(--sl-text-xs);
|
|
548
|
+
font-weight: 500;
|
|
549
|
+
line-height: 1.3;
|
|
550
|
+
opacity: 0;
|
|
551
|
+
pointer-events: none;
|
|
552
|
+
transform: translateY(-50%) translateX(-0.2rem);
|
|
553
|
+
transition:
|
|
554
|
+
opacity 100ms ease,
|
|
555
|
+
transform 100ms ease;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
.layout-compact-sidebar
|
|
559
|
+
details[data-compact]
|
|
560
|
+
> ul:not(.top-level)
|
|
561
|
+
> li
|
|
562
|
+
> a:is(:hover, :focus-visible)
|
|
563
|
+
> span {
|
|
564
|
+
opacity: 1;
|
|
565
|
+
transform: translateY(-50%) translateX(0);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
.layout-compact-sidebar details[data-compact] > summary .caret {
|
|
569
|
+
transform: rotateZ(0);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
[dir='rtl']
|
|
573
|
+
.layout-compact-sidebar
|
|
574
|
+
details[data-compact]
|
|
575
|
+
> summary
|
|
576
|
+
.caret {
|
|
577
|
+
transform: rotateZ(180deg);
|
|
578
|
+
}
|
|
579
|
+
|
|
451
580
|
.layout-navigation-toggle {
|
|
452
581
|
position: fixed;
|
|
453
582
|
z-index: calc(var(--sl-z-index-menu) + 1);
|