Skip to content

Commit 2bc8805

Browse files
committed
Improve style of ordered and unordered lists
1 parent 840958d commit 2bc8805

16 files changed

Lines changed: 374 additions & 132 deletions

.patch-tool-add-test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ok2

_TODO.md

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -202,12 +202,55 @@ Let's work through each article section by section based on the H2 headers in th
202202
## Stylings
203203

204204
- Better appearance of details and summary elements. The styling in rehype-tailwind is applying to the detail and summary elements generated by remark-custom-blocks.
205-
- Better list styling and decoration markers
206-
- Remove dotted underline from abbr elements? Or some other way of indicating abbreviations?
207205
- [Link](url) [[color=pink target=_blank .centered]] is not picking up styles
208-
- We have remark-mark-plus to create marked text like ==mark me==. Is the Highlighter component our social highlighter?
206+
- remark-mark-plus highlight color needs adjusted.
207+
- Highlighter component not rendering social share modal on hover and focus.
209208
- Are we using breakpoints correctly - isn't iPhone wider than our `sm` breakpoint?
210-
- Mermaid theming, horizontal layout for desktop, vertical for mobile, using groups like in Code Tabs to add the media selectors e.g. `[g1:desktop]`
209+
- Add scroll bar under header to show how far down you are on the page while reading
210+
211+
## Add theming to Mermaid
212+
213+
Mermaid theming, horizontal layout for desktop, vertical for mobile, using groups like in Code Tabs to add the media selectors e.g. `[g1:desktop]`
211214

212215
`graph TD`: Top-to-Bottom (Vertical).
213216
`graph LR`: Left-to-Right (Horizontal).
217+
218+
`strategy: 'pre-mermaid'` in Mermaid config in `astro.config.ts`
219+
220+
This strategy replaces the element with a <pre class="mermaid"> element with only the diagram as its child. Given the example, this yields:
221+
222+
```html
223+
<html>
224+
<head>
225+
<meta charset="utf-8" />
226+
</head>
227+
<body>
228+
<pre class="mermaid">
229+
graph TD;
230+
A-->B;
231+
A-->C;
232+
B-->D;
233+
C-->D;
234+
</pre>
235+
<pre class="mermaid">
236+
graph TD;
237+
A-->B;
238+
A-->C;
239+
B-->D;
240+
C-->D;
241+
</pre>
242+
</body>
243+
</html>
244+
```
245+
246+
This allows Mermaid to render the diagram on the client side, for example using:
247+
248+
````typescript
249+
import mermaid from 'mermaid'
250+
251+
mermaid.initialize({ startOnLoad: true })
252+
```
253+
254+
[rerender diagrams when I switched color-scheme](https://github.com/mermaid-js/mermaid/issues/1945)
255+
[mermaid init](https://github.com/hbstack/mermaid/blob/main/assets/hb/modules/mermaid/init.ts)
256+
[theming](https://mermaid.ai/open-source/config/theming.html)

package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@
165165
"lit": "^3.3.2",
166166
"markdownlint-cli2": "^0.20.0",
167167
"md-attr-parser": "^1.3.0",
168+
"mermaid": "^11.12.2",
168169
"nanostores": "^1.1.0",
169170
"nodemailer": "^7.0.12",
170171
"npm": "^11.7.0",

src/content/articles/demo/index.mdx

Lines changed: 46 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -13,50 +13,6 @@ import Demo from '@components/Test/Demo.mdx'
1313

1414
## Components (looping)
1515

16-
### Images
17-
18-
```markdown
19-
![Octocat](https://myoctocat.com/assets/images/base-octocat.svg)
20-
```
21-
22-
![Octocat](https://myoctocat.com/assets/images/base-octocat.svg)
23-
24-
Linked image:
25-
26-
```markdown
27-
[![An old rock in the desert](/assets/images/shiprock.svg "Shiprock placeholder")](https://www.flickr.com/photos/beaurogers/31833779864/in/photolist-Qv3rFw-34mt9F-a9Cmfy-5Ha3Zi-9msKdv-o3hgjr-hWpUte-4WMsJ1-KUQ8N-deshUb-vssBD-6CQci6-8AFCiD-zsJWT-nNfsgB-dPDwZJ-bn9JGn-5HtSXY-6CUhAL-a4UTXB-ugPum-KUPSo-fBLNm-6CUmpy-4WMsc9-8a7D3T-83KJev-6CQ2bK-nNusHJ-a78rQH-nw3NvT-7aq2qf-8wwBso-3nNceh-ugSKP-4mh4kh-bbeeqH-a7biME-q3PtTf-brFpgb-cg38zw-bXMZc-nJPELD-f58Lmo-bXMYG-bz8AAi-bxNtNT-bXMYi-bXMY6-bXMYv)
28-
```
29-
30-
[![An old rock in the desert](/assets/images/shiprock.svg "Shiprock placeholder")](https://www.flickr.com/photos/beaurogers/31833779864/in/photolist-Qv3rFw-34mt9F-a9Cmfy-5Ha3Zi-9msKdv-o3hgjr-hWpUte-4WMsJ1-KUQ8N-deshUb-vssBD-6CQci6-8AFCiD-zsJWT-nNfsgB-dPDwZJ-bn9JGn-5HtSXY-6CUhAL-a4UTXB-ugPum-KUPSo-fBLNm-6CUmpy-4WMsc9-8a7D3T-83KJev-6CQ2bK-nNusHJ-a78rQH-nw3NvT-7aq2qf-8wwBso-3nNceh-ugSKP-4mh4kh-bbeeqH-a7biME-q3PtTf-brFpgb-cg38zw-bXMZc-nJPELD-f58Lmo-bXMYG-bz8AAi-bxNtNT-bXMYi-bXMY6-bXMYv)
31-
32-
### `kbd`
33-
34-
This is a default HTML markup, just adding it here to not forget to use it when needed:
35-
36-
```markdown
37-
<p>
38-
You can also create a new document by pressing the
39-
<kbd><kbd>Ctrl</kbd>+<kbd>N</kbd></kbd> shortcut.
40-
</p>
41-
```
42-
43-
<p>
44-
You can also create a new document by pressing the
45-
<kbd><kbd>Ctrl</kbd>+<kbd>N</kbd></kbd> shortcut.
46-
</p>
47-
48-
### Links
49-
50-
```markdown
51-
My favorite search engine is [Duck Duck Go](https://duckduckgo.com).
52-
53-
Visit our [homepage](/) or read more [articles](/articles) to explore additional content.
54-
```
55-
56-
My favorite search engine is [Duck Duck Go](https://duckduckgo.com).
57-
58-
Visit our [homepage](/) or read more [articles](/articles) to explore additional content.
59-
6016
### Lists
6117

6218
Unordered lists:
@@ -1414,3 +1370,49 @@ Dashes (`---`):
14141370
Underscores ()`___`):
14151371

14161372
_________________
1373+
1374+
### Images
1375+
1376+
Use a relative path from the folder if the image is in src/assets (e.g. `../../../assets/images/typescript-downloadable-icon.png`) and will be optimized in the build. Images in the public folder are not optimized and can be referenced with an absolute path like `shiprock.svg` below.
1377+
1378+
```markdown
1379+
![Shiprock](/assets/images/shiprock.svg)
1380+
```
1381+
1382+
![Shiprock](/assets/images/shiprock.svg)
1383+
1384+
Linked image:
1385+
1386+
```markdown
1387+
[![Shiprock](/assets/images/shiprock.svg "Shiprock placeholder")](/contact)
1388+
```
1389+
1390+
[![Shiprock](/assets/images/shiprock.svg "Shiprock placeholder")](/contact)
1391+
1392+
### `kbd`
1393+
1394+
This is a default HTML markup, just adding it here to not forget to use it when needed:
1395+
1396+
```markdown
1397+
<p>
1398+
You can also create a new document by pressing the
1399+
<kbd><kbd>Ctrl</kbd>+<kbd>N</kbd></kbd> shortcut.
1400+
</p>
1401+
```
1402+
1403+
<p>
1404+
You can also create a new document by pressing the
1405+
<kbd><kbd>Ctrl</kbd>+<kbd>N</kbd></kbd> shortcut.
1406+
</p>
1407+
1408+
### Links
1409+
1410+
```markdown
1411+
My favorite search engine is [Duck Duck Go](https://duckduckgo.com).
1412+
1413+
Visit our [homepage](/) or read more [articles](/articles) to explore additional content.
1414+
```
1415+
1416+
My favorite search engine is [Duck Duck Go](https://duckduckgo.com).
1417+
1418+
Visit our [homepage](/) or read more [articles](/articles) to explore additional content.

src/lib/markdown/__tests__/e2e/rehype-tailwind-classes.spec.tsx

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,20 @@ function expectHasAtLeastOneValidClassAttribute(element: Element | null): void {
4747
})
4848
}
4949

50+
function expectClassTokensAreWellFormedIfPresent(element: Element | null): void {
51+
expect(element).toBeTruthy()
52+
if (!element) return
53+
54+
const classAttr = element.getAttribute('class')
55+
if (!classAttr) return
56+
57+
const tokens = splitClassTokens(classAttr)
58+
expect(tokens.length).toBeGreaterThan(0)
59+
tokens.forEach(token => {
60+
expect(isWellFormedCssClassToken(token)).toBe(true)
61+
})
62+
}
63+
5064
let html: string
5165

5266
beforeAll(async () => {
@@ -107,18 +121,21 @@ describe('Layer 3: E2E - rehypeTailwindClasses', () => {
107121
expectHasAtLeastOneValidClassAttribute(paragraph)
108122
})
109123

110-
it('should add Tailwind classes to lists', () => {
124+
it('should wrap markdown lists for scoped styling', () => {
111125
const { container } = render(<MarkdownOutput html={html} />)
112126

113-
const list = container.querySelector('ul')
114-
expectHasAtLeastOneValidClassAttribute(list)
127+
const listWrapper = container.querySelector('div.markdown-list')
128+
expectHasAtLeastOneValidClassAttribute(listWrapper)
129+
130+
const list = listWrapper?.querySelector('ul')
131+
expect(list).toBeTruthy()
115132
})
116133

117-
it('should add Tailwind classes to list items', () => {
134+
it('should keep list items semantic (classes optional)', () => {
118135
const { container } = render(<MarkdownOutput html={html} />)
119136

120137
const listItem = container.querySelector('li')
121-
expectHasAtLeastOneValidClassAttribute(listItem)
138+
expectClassTokensAreWellFormedIfPresent(listItem)
122139
})
123140

124141
it('should add Tailwind classes to code elements', () => {

src/lib/markdown/__tests__/integration/rehype-tailwind-classes-astro.spec.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ describe('rehype-tailwind-classes (Layer 2: With Astro Pipeline)', () => {
4949
})
5050

5151
expect(html).toContain('<ul')
52-
expect(html).toMatch(/class="[^"]*list-/)
52+
expect(html).toMatch(/<div class="markdown-list[^\"]*">\s*<ul/)
53+
expect(html).toMatch(/contains-task-list|task-list-item/)
5354
})
5455
})
5556

@@ -68,7 +69,8 @@ Text with footnote[^1]
6869
})
6970

7071
expect(html).toContain('class=')
71-
expect(html).toMatch(/mb-8|text-lg/)
72+
expect(html).toMatch(/class="[^"]*mb-/)
73+
expect(html).toMatch(/<div class="markdown-list[^\"]*">\s*<ol>/)
7274
})
7375
})
7476

src/lib/markdown/__tests__/integration/rehype-tailwind-simple-elements-astro.spec.ts

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,12 @@ function expectHasAtLeastOneValidClassAttribute(element: Element | null, name: s
4747
}
4848

4949
async function renderMarkdownToDocument(markdown: string): Promise<Document> {
50-
const html = await processWithAstroSettings({ markdown, plugin: rehypeTailwindClasses, stage: 'rehype' })
50+
const html = await processWithAstroSettings({
51+
markdown,
52+
plugin: rehypeTailwindClasses,
53+
stage: 'rehype',
54+
})
55+
5156
return new JSDOM(html).window.document
5257
}
5358

@@ -68,14 +73,19 @@ describe('rehypeTailwindClasses (Layer 2: Astro Pipeline) - simple elements', ()
6873
expectHasAtLeastOneValidClassAttribute(starred.querySelector('hr'), 'hr (***)')
6974
})
7075

71-
it('adds at least one class to <ul>, <ol>, and <li> elements', async () => {
72-
const document = await renderMarkdownToDocument(['- Item 1', '- Item 2', '', '1. First', '2. Second'].join('\n'))
76+
it('wraps top-level lists in a .markdown-list container', async () => {
77+
const markdown = ['- Item 1', '- Item 2', '', '1. First', '2. Second'].join('\n')
78+
const document = await renderMarkdownToDocument(markdown)
7379

74-
expectHasAtLeastOneValidClassAttribute(document.querySelector('ul'), 'ul')
75-
expectHasAtLeastOneValidClassAttribute(document.querySelector('ol'), 'ol')
80+
const listWrappers = Array.from(document.querySelectorAll('div.markdown-list'))
81+
expect(listWrappers.length).toBeGreaterThanOrEqual(2)
82+
listWrappers.forEach((wrapper, index) => {
83+
expectHasAtLeastOneValidClassAttribute(wrapper, `div.markdown-list[${index}]`)
84+
})
7685

77-
const firstListItem = document.querySelector('li')
78-
expectHasAtLeastOneValidClassAttribute(firstListItem, 'li')
86+
expect(document.querySelector('ul')).toBeTruthy()
87+
expect(document.querySelector('ol')).toBeTruthy()
88+
expect(document.querySelector('li')).toBeTruthy()
7989
})
8090

8191
it('adds at least one class to <table>, <th>, and <td> elements', async () => {
@@ -95,9 +105,19 @@ describe('rehypeTailwindClasses (Layer 2: Astro Pipeline) - simple elements', ()
95105

96106
it('does not emit malformed class tokens anywhere', async () => {
97107
const document = await renderMarkdownToDocument(
98-
['# Heading', '', 'Paragraph text.', '', '- List item', '', '---', '', '| A | B |', '| --- | --- |', '| 1 | 2 |'].join(
99-
'\n'
100-
)
108+
[
109+
'# Heading',
110+
'',
111+
'Paragraph text.',
112+
'',
113+
'- List item',
114+
'',
115+
'---',
116+
'',
117+
'| A | B |',
118+
'| --- | --- |',
119+
'| 1 | 2 |',
120+
].join('\n')
101121
)
102122

103123
const withClass = Array.from(document.querySelectorAll('[class]'))

src/lib/markdown/__tests__/integration/remark-captions-astro.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ describe('remark-captions (Layer 2: Astro Pipeline)', () => {
3131
pluginOptions: remarkCaptionsConfig,
3232
})
3333

34-
expect(html.match(/<figure>/g)?.length).toBeGreaterThanOrEqual(4)
34+
expect(html.match(/<figure>/g)?.length).toBeGreaterThanOrEqual(3)
3535
expect(html).toContain('Quote caption')
3636
expect(html).toContain('Table caption')
3737
expect(html).toContain('Code caption')

src/lib/markdown/plugins/rehype-tailwind/__tests__/index.spec.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -70,21 +70,15 @@ describe('htmlElements configuration', () => {
7070

7171
test('includes all expected element types', () => {
7272
const expectedElements = [
73-
'p',
74-
'img',
75-
'video',
76-
'figure',
77-
'figcaption',
73+
'details',
7874
'hr',
79-
'ul',
80-
'ol',
81-
'li',
75+
'img',
8276
'mark',
77+
'summary',
8378
'table',
8479
'th',
8580
'td',
86-
'summary',
87-
'details',
81+
'video',
8882
]
8983

9084
expectedElements.forEach(tagName => {
@@ -213,9 +207,9 @@ describe('isSimpleHtmlElement', () => {
213207

214208
describe('getElementConfig', () => {
215209
test('returns config for valid tag name', () => {
216-
const config = getElementConfig('p')
210+
const config = getElementConfig('img')
217211
expect(config).toBeDefined()
218-
expect(config?.tagName).toBe('p')
212+
expect(config?.tagName).toBe('img')
219213
expect(Array.isArray(config?.classes)).toBe(true)
220214
expect((config?.classes || []).length).toBeGreaterThan(0)
221215
;(config?.classes || []).forEach(classToken => {

0 commit comments

Comments
 (0)