Skip to main content

euv_ui/style/css/
const.rs

1/// Raw stylesheet for rendered markdown bodies ([`crate::euv_markdown`]).
2///
3/// `class!` cannot express descendant selectors, so markdown typography is
4/// injected once at startup through `Css::inject_css`. All colors reference
5/// the theme CSS variables, so light/dark switching works unchanged.
6pub const EUV_MD_CSS: &str = r#"
7.md-body {
8    line-height: 1.7;
9    font-size: var(--font-base);
10    word-wrap: break-word;
11}
12.md-body h1, .md-body h2, .md-body h3, .md-body h4, .md-body h5, .md-body h6 {
13    position: relative;
14    font-weight: 700;
15    letter-spacing: -0.01em;
16    margin-top: 1.8em;
17    margin-bottom: 0.6em;
18    scroll-margin-top: 72px;
19    line-height: 1.3;
20}
21.md-body h1 {
22    font-size: var(--font-3xl);
23    margin-top: 0;
24    padding-bottom: 0.4em;
25    border-bottom: 1px solid var(--border);
26}
27.md-body h2 {
28    font-size: var(--font-2xl);
29    padding-bottom: 0.3em;
30    border-bottom: 1px dashed var(--border);
31}
32.md-body h3 { font-size: var(--font-xl); }
33.md-body h4 { font-size: var(--font-lg); }
34.md-body h5, .md-body h6 { font-size: var(--font-base); }
35.md-body .header-anchor {
36    float: left;
37    margin-left: -0.9em;
38    padding-right: 0.2em;
39    opacity: 0;
40    color: var(--muted-foreground);
41    font-weight: 400;
42    transition: opacity 0.15s ease-out;
43    user-select: none;
44    /* The `.md-body a` rule below applies `text-decoration: underline
45       dashed` to every anchor. The header-anchor is a typographic
46       icon, not a navigable link in the visual sense, so drop the
47       underline. */
48    text-decoration: none;
49}
50.md-body h1:hover .header-anchor,
51.md-body h2:hover .header-anchor,
52.md-body h3:hover .header-anchor,
53.md-body h4:hover .header-anchor,
54.md-body h5:hover .header-anchor,
55.md-body h6:hover .header-anchor {
56    opacity: 1;
57}
58/* On narrow viewports the negative `margin-left: -0.9em` pushes the `#`
59   sign past the left edge of the viewport (anchor x ≈ -11px on a 380px
60   viewport), so hover-revealed anchors get clipped. On mobile we drop
61   the float + negative margin and absolutely position the anchor at
62   the heading's left edge instead, with the heading content shifted
63   right via `padding-left`. This way:
64
65   * The `#` stays inside the viewport at all times.
66   * The `#` baseline aligns with the heading's first line baseline
67     (`vertical-align: middle` on a single-line-height inline-flex
68     box puts the `#` on the same horizontal line as the heading
69     text, not against the heading's overall vertical centre).
70   * The heading text gets the full content width (minus the small
71     reserved gutter), so a long heading like "Markdown Features" no
72     longer wraps with one orphan word on its own line leaving a big
73     empty space at the end of the first line.
74
75   Earlier revisions used `height: 100%; align-items: center`, which
76   stretched the anchor box to the full heading height and put the
77   `#` on the heading's geometric centre. For a wrapped h1 like
78   "Markdown Features" that ended up aligned with the second line,
79   not the first — visually it looked like the `#` belonged to
80   "Features" rather than "Markdown". */
81@media (max-width: 767px) {
82    /* On narrow viewports the negative `margin-left: -0.9em` on the
83       anchor pushes the `#` sign past the left edge of the viewport
84       (anchor x ≈ -11px on a 380px viewport), so hover-revealed
85       anchors get clipped. We rework the mobile anchor to live
86       inside the heading's inline flow instead of floating to the
87       left:
88
89       * The heading itself gets a `padding-left` so its inline
90         content (the actual heading text) still starts at the same
91         visual x as before.
92       * The anchor is the FIRST inline child of the heading, with
93         a `margin-left: -1.6em` so it pulls itself into the gutter
94         reserved by that padding.
95       * With `vertical-align: baseline` and a single-line-height
96         box (`line-height: 1`), the `#` glyph baseline lines up
97         with the heading text baseline on the FIRST line — so the
98         `#` and "Markdown" share the same horizontal line, instead
99         of the `#` being vertically centred against the whole
100         heading box (which, for wrapped headings like "Markdown
101         Features", used to put `#` on the line between the two
102         words).
103       * The heading text gets the full content width (minus the
104         gutter), so a long heading like "Markdown Features" no
105         longer wraps with one orphan word on its own line leaving
106         a big empty space at the end of the first line — because
107         the anchor no longer occupies any inline space on the
108         first line. */
109    .md-body h1,
110    .md-body h2,
111    .md-body h3,
112    .md-body h4,
113    .md-body h5,
114    .md-body h6 {
115        padding-left: 1.6em;
116    }
117    .md-body .header-anchor {
118        float: none;
119        margin-left: -1.6em;
120        margin-right: 0;
121        padding: 0;
122        width: 1.6em;
123        display: inline-flex;
124        align-items: flex-end;
125        justify-content: flex-start;
126        font-size: 0.85em;
127        line-height: 1;
128        vertical-align: baseline;
129    }
130}
131.md-body p, .md-body ul, .md-body ol, .md-body blockquote, .md-body pre, .md-body table {
132    margin: 1em 0;
133}
134.md-body ul, .md-body ol {
135    padding-left: 1.4em;
136}
137.md-body ul { list-style: disc; }
138.md-body ol { list-style: decimal; }
139.md-body ul ul, .md-body ul ol, .md-body ol ul, .md-body ol ol {
140    margin: 0.25em 0;
141}
142.md-body li { margin: 0.25em 0; }
143.md-body li input[type="checkbox"] {
144    margin-right: 0.4em;
145    accent-color: var(--accent);
146}
147.md-body a {
148    color: var(--accent);
149    font-weight: 500;
150    text-decoration: underline;
151    text-underline-offset: 3px;
152    text-decoration-style: dashed;
153    text-decoration-color: var(--border);
154}
155.md-body a:hover {
156    text-decoration-style: solid;
157    text-decoration-color: var(--accent);
158}
159.md-body strong { font-weight: 700; }
160.md-body em { font-style: italic; }
161.md-body del { opacity: 0.6; }
162.md-body hr {
163    border: none;
164    border-top: 1px dashed var(--border);
165    margin: 2em 0;
166}
167.md-body blockquote {
168    margin: 1em 0;
169    padding: 0.4em 1em;
170    border-left: 4px solid var(--border);
171    color: var(--muted-foreground);
172}
173.md-body blockquote p { margin: 0.4em 0; }
174.md-body code {
175    font-family: ui-monospace, monospace;
176    font-size: 0.875em;
177    padding: 0.15em 0.4em;
178    background: var(--accent-muted);
179    border: 1px solid var(--border);
180    /* Inline <code> that wraps to multiple lines must keep its border
181       on every fragment, otherwise the first line loses its right border
182       and subsequent lines lose their left border.
183
184       box-decoration-break: clone alone is not enough in practice: the
185       per-fragment borders stack against the next fragment's background
186       and the visual gap between fragments collapses, making the right
187       border of one line look like the left border of the next (and
188       neither looks fully closed). Making the element inline-block
189       guarantees each fragment draws its own box with its own four
190       borders, exactly the same trick used by euv_tag. */
191    -webkit-box-decoration-break: clone;
192    box-decoration-break: clone;
193    display: inline-block;
194    /* `vertical-align: baseline` plus `line-height: 1` keeps the
195       <code> box visually on the same baseline as the surrounding
196       text. Earlier revisions used `vertical-align: text-top` with
197       `line-height: 1.4`, which pushed the box downward and made the
198       framed text look like it was sitting on a lower line than the
199       surrounding body text — a small offset, but very noticeable in
200       tight running prose like list items and paragraphs. With
201       `line-height: 1` the inline-block has no extra leading so its
202       baseline aligns with the baseline of the parent line. */
203    vertical-align: baseline;
204    line-height: 1;
205}
206/* In a table cell an inline-block <code> collapses to the width of
207   its longest word plus padding, leaving a tall narrow box with large
208   blank gaps on every other line. Promote it to `display: block` so
209   the box fills the cell and the text wraps naturally, while still
210   preserving the per-fragment border from the rule above.
211
212   We do NOT use `td > code` here. euv-docs / VuePress insert
213   `display: contents` <div> / <slot> wrappers between the cell and
214   the <code> element at runtime, so the direct-child selector
215   never matches in the deployed build. Using `td code` instead
216   reaches the same <code> regardless of those transparent wrappers.
217
218   Specificity check: `.md-body td code` is (0,1,2) and the base
219   `.md-body code` rule is (0,1,1), so this rule wins without
220   needing `!important` or repeated `.md-body` classes. */
221.md-body td code,
222.md-body th code {
223    display: block;
224}
225.md-body pre {
226    padding: 1em 1.2em;
227    overflow-x: auto;
228    border: 1px solid var(--border);
229    background: var(--accent-muted);
230}
231.md-body pre code {
232    padding: 0;
233    border: none;
234    background: transparent;
235    font-size: 0.875rem;
236    line-height: 1.6;
237}
238.md-body table {
239    width: 100%;
240    border-collapse: collapse;
241    font-size: var(--font-sm);
242    display: block;
243    overflow-x: auto;
244}
245.md-body table thead { border-bottom: 2px solid var(--border); }
246.md-body table th, .md-body table td {
247    padding: 0.5em 0.9em;
248    border: 1px solid var(--border);
249    text-align: left;
250}
251.md-body table th { font-weight: 700; }
252.md-body table tbody tr:nth-child(2n) { background: var(--accent-muted); }
253.md-body img { max-width: 100%; }
254.md-body .docs-container {
255    margin: 1.2em 0;
256    padding: 0.1em 1.2em;
257    border-left: 4px solid var(--foreground);
258    background: var(--accent-muted);
259}
260.md-body .docs-container .docs-container-title {
261    font-weight: 700;
262    font-size: var(--font-sm);
263    letter-spacing: 0.04em;
264    text-transform: uppercase;
265    margin: 0.8em 0 0.4em;
266}
267.md-body .docs-container.tip { border-left-style: solid; }
268.md-body .docs-container.warning { border-left-style: dashed; }
269.md-body .docs-container.danger {
270    border-left: 4px double var(--foreground);
271}
272.md-body .docs-container.details {
273    border-left: 1px solid var(--border);
274    background: transparent;
275}
276.md-body .footnote-definition { font-size: var(--font-sm); color: var(--muted-foreground); }
277"#;