@schalkneethling/toolkit 0.1.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/LICENSE +21 -0
- package/README.md +103 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +292 -0
- package/dist/index.mjs.map +1 -0
- package/hooks/block-dangerous-commands/hook.js +120 -0
- package/hooks/block-dangerous-commands/settings-fragment.json +15 -0
- package/package.json +35 -0
- package/skills/semantic-html/SKILL.md +603 -0
- package/skills/semantic-html/references/element-decision-trees.md +111 -0
- package/skills/semantic-html/references/heading-patterns.md +275 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Element Decision Trees
|
|
2
|
+
|
|
3
|
+
Quick decision frameworks for selecting the right HTML element.
|
|
4
|
+
|
|
5
|
+
## Is It a List?
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Does knowing the count help the user?
|
|
9
|
+
├─ Yes → Are items sequential/ranked?
|
|
10
|
+
│ ├─ Yes → <ol>
|
|
11
|
+
│ └─ No → <ul>
|
|
12
|
+
├─ No → Are these term-description pairs?
|
|
13
|
+
│ ├─ Yes → <dl>
|
|
14
|
+
│ └─ No → Consider plain elements with appropriate structure
|
|
15
|
+
└─ Is it a toolbar of commands?
|
|
16
|
+
└─ Yes → <menu>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Is It a Table?
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
Does data have meaningful rows AND columns?
|
|
23
|
+
├─ No → Not a table
|
|
24
|
+
│ ├─ One dimension only → List or plain elements
|
|
25
|
+
│ ├─ Key-value pairs → <dl>
|
|
26
|
+
│ └─ Hierarchical → Nested lists or other structure
|
|
27
|
+
└─ Yes → Does each row have the same columns?
|
|
28
|
+
├─ No → Reconsider data structure
|
|
29
|
+
└─ Yes → Use <table> with full semantics
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Button or Link?
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
Does this navigate to a new URL?
|
|
36
|
+
├─ Yes → <a href="...">
|
|
37
|
+
└─ No → Does an action occur?
|
|
38
|
+
├─ Yes → Would a URL provide useful fallback if JS fails?
|
|
39
|
+
│ ├─ Yes → <a href="..."> with JS enhancement
|
|
40
|
+
│ └─ No → <button>
|
|
41
|
+
└─ No → Reconsider if interaction is needed
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Section or Div?
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
Does this content form a thematic grouping?
|
|
48
|
+
├─ No → <div>
|
|
49
|
+
└─ Yes → Can you provide a meaningful accessible name?
|
|
50
|
+
├─ Yes → <section aria-labelledby="...">
|
|
51
|
+
└─ No → <div> (section without label ≈ div semantically)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Article Candidate?
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
Would this content make sense standalone?
|
|
58
|
+
├─ Yes → Could it be syndicated or extracted?
|
|
59
|
+
│ ├─ Yes → <article>
|
|
60
|
+
│ └─ No → Use appropriate container (section, div, etc.)
|
|
61
|
+
└─ No → Use appropriate container (section, div, etc.)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Which Landmark?
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
What is this section's purpose?
|
|
68
|
+
├─ Site/section header → <header>
|
|
69
|
+
├─ Site/section footer → <footer>
|
|
70
|
+
├─ Navigation → <nav> (must be labelled)
|
|
71
|
+
├─ Primary page content → <main>
|
|
72
|
+
├─ Search/filter functionality → <search> (wrap ALL related controls, not just the text input)
|
|
73
|
+
├─ User input → <form> (must be labelled to be a landmark)
|
|
74
|
+
├─ Tangentially related → <aside>
|
|
75
|
+
├─ Self-contained content → <article>
|
|
76
|
+
└─ Thematic grouping with label → <section aria-labelledby="...">
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Form Field Grouping
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
Do these fields form a logical/thematic group?
|
|
83
|
+
├─ Yes → Are they form controls (inputs, selects, checkboxes)?
|
|
84
|
+
│ ├─ Yes → <fieldset> with <legend>
|
|
85
|
+
│ └─ No → <section aria-labelledby="...">
|
|
86
|
+
└─ No → Is visual grouping needed?
|
|
87
|
+
├─ Yes → <div> with appropriate styling
|
|
88
|
+
└─ No → Fields can exist without wrapper
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Interactive Disclosure
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
Should content be expandable/collapsible?
|
|
95
|
+
├─ Yes → Does the disclosed content need ARIA roles (menu, dialog, etc.)?
|
|
96
|
+
│ ├─ Yes → <button> controlling visibility + appropriate ARIA
|
|
97
|
+
│ └─ No → Is it a single content section?
|
|
98
|
+
│ ├─ Yes → <details>/<summary>
|
|
99
|
+
│ └─ No → Consider tab pattern or custom disclosure
|
|
100
|
+
└─ No → Is it a list of definitions?
|
|
101
|
+
└─ Yes → <dl> (not details/summary)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Skip Navigation
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
Does the page have a <nav> or repeated content before <main>?
|
|
108
|
+
└─ Yes → Add <a href="#main-content"> as the first element in <body>
|
|
109
|
+
Does the page have a prominent search bar or long sidebar?
|
|
110
|
+
└─ Yes → Add additional skip links to those targets
|
|
111
|
+
```
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# Heading Patterns in Component Systems
|
|
2
|
+
|
|
3
|
+
Strategies for maintaining proper heading hierarchy in component-based architectures.
|
|
4
|
+
|
|
5
|
+
## The Challenge
|
|
6
|
+
|
|
7
|
+
Component-based development creates a tension:
|
|
8
|
+
|
|
9
|
+
- Components should be reusable across contexts
|
|
10
|
+
- Heading levels depend on surrounding document structure
|
|
11
|
+
- Content authors may not understand heading hierarchy
|
|
12
|
+
- Hardcoded levels break in different contexts
|
|
13
|
+
|
|
14
|
+
## Pattern 1: Configurable Heading Level
|
|
15
|
+
|
|
16
|
+
Make heading level a prop/parameter with a sensible default.
|
|
17
|
+
|
|
18
|
+
```jsx
|
|
19
|
+
// React example
|
|
20
|
+
function Card({ title, headingLevel = 3, children }) {
|
|
21
|
+
const Heading = `h${headingLevel}`;
|
|
22
|
+
return (
|
|
23
|
+
<article className="card">
|
|
24
|
+
<Heading>{title}</Heading>
|
|
25
|
+
{children}
|
|
26
|
+
</article>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```twig
|
|
32
|
+
{# Twig example #}
|
|
33
|
+
{% set heading_tag = heading_level|default(3) %}
|
|
34
|
+
<article class="card">
|
|
35
|
+
<h{{ heading_tag }}>{{ title }}</h{{ heading_tag }}>
|
|
36
|
+
{{ content }}
|
|
37
|
+
</article>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### When to Use
|
|
41
|
+
|
|
42
|
+
- Generic components used in multiple contexts
|
|
43
|
+
- Components where nesting depth varies
|
|
44
|
+
- CMS-driven content where authors control usage
|
|
45
|
+
|
|
46
|
+
### Trade-offs
|
|
47
|
+
|
|
48
|
+
- Moves responsibility to component consumer
|
|
49
|
+
- Authors may not choose correctly
|
|
50
|
+
- Sensible default reduces but doesn't eliminate risk
|
|
51
|
+
|
|
52
|
+
## Pattern 2: Context-Aware Defaults
|
|
53
|
+
|
|
54
|
+
Set defaults based on known component relationships.
|
|
55
|
+
|
|
56
|
+
```jsx
|
|
57
|
+
// Section always starts a new heading context
|
|
58
|
+
function Section({ title, children }) {
|
|
59
|
+
return (
|
|
60
|
+
<section aria-labelledby="section-title">
|
|
61
|
+
<h2 id="section-title">{title}</h2>
|
|
62
|
+
{children}
|
|
63
|
+
</section>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Cards within sections default to h3
|
|
68
|
+
function CardList({ cards }) {
|
|
69
|
+
return (
|
|
70
|
+
<ul className="card-list">
|
|
71
|
+
{cards.map((card) => (
|
|
72
|
+
<li key={card.id}>
|
|
73
|
+
<Card title={card.title} headingLevel={3} />
|
|
74
|
+
</li>
|
|
75
|
+
))}
|
|
76
|
+
</ul>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### When to Use
|
|
82
|
+
|
|
83
|
+
- Known parent-child component relationships
|
|
84
|
+
- Design systems with predictable nesting patterns
|
|
85
|
+
- When you control both container and child components
|
|
86
|
+
|
|
87
|
+
### Trade-offs
|
|
88
|
+
|
|
89
|
+
- Less flexible
|
|
90
|
+
- Breaks if components are used outside expected context
|
|
91
|
+
- Requires documentation of expected usage
|
|
92
|
+
|
|
93
|
+
## Pattern 3: Heading Component Abstraction
|
|
94
|
+
|
|
95
|
+
Create a heading component that handles both semantic and visual concerns.
|
|
96
|
+
|
|
97
|
+
```jsx
|
|
98
|
+
function Heading({ level, visualLevel = level, children, className = "" }) {
|
|
99
|
+
const Tag = `h${level}`;
|
|
100
|
+
const visualClass = `u-heading-${visualLevel}`;
|
|
101
|
+
|
|
102
|
+
return <Tag className={`${visualClass} ${className}`}>{children}</Tag>;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Usage: semantic h3, visual appearance of h2
|
|
106
|
+
<Heading level={3} visualLevel={2}>
|
|
107
|
+
Section Title
|
|
108
|
+
</Heading>;
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### When to Use
|
|
112
|
+
|
|
113
|
+
- Design requires visual hierarchy different from semantic
|
|
114
|
+
- Large headings needed at deep nesting levels
|
|
115
|
+
- Consistent visual treatment across varying semantic levels
|
|
116
|
+
|
|
117
|
+
### Benefits
|
|
118
|
+
|
|
119
|
+
- Separates concerns clearly
|
|
120
|
+
- Documents the distinction explicitly
|
|
121
|
+
- Enables correct semantics without design compromise
|
|
122
|
+
|
|
123
|
+
## Pattern 4: Inherited Configuration
|
|
124
|
+
|
|
125
|
+
Generic components inherit heading config when specialised.
|
|
126
|
+
|
|
127
|
+
```jsx
|
|
128
|
+
// Generic card
|
|
129
|
+
function Card({ title, headingLevel = 3, headingClass, children }) {
|
|
130
|
+
const Heading = `h${headingLevel}`;
|
|
131
|
+
return (
|
|
132
|
+
<article className="card">
|
|
133
|
+
<Heading className={headingClass}>{title}</Heading>
|
|
134
|
+
{children}
|
|
135
|
+
</article>
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Specialised product card - knows its context
|
|
140
|
+
function ProductCard({ product, headingLevel = 3 }) {
|
|
141
|
+
return (
|
|
142
|
+
<Card
|
|
143
|
+
title={product.name}
|
|
144
|
+
headingLevel={headingLevel}
|
|
145
|
+
headingClass="product-card__title"
|
|
146
|
+
>
|
|
147
|
+
<p className="product-card__price">{product.price}</p>
|
|
148
|
+
<p className="product-card__description">{product.description}</p>
|
|
149
|
+
</Card>
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Page section - sets context for children
|
|
154
|
+
function FeaturedProducts({ products }) {
|
|
155
|
+
return (
|
|
156
|
+
<section aria-labelledby="featured-heading">
|
|
157
|
+
<h2 id="featured-heading">Featured Products</h2>
|
|
158
|
+
<ul className="product-grid">
|
|
159
|
+
{products.map((product) => (
|
|
160
|
+
<li key={product.id}>
|
|
161
|
+
<ProductCard product={product} headingLevel={3} />
|
|
162
|
+
</li>
|
|
163
|
+
))}
|
|
164
|
+
</ul>
|
|
165
|
+
</section>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### When to Use
|
|
171
|
+
|
|
172
|
+
- Design systems with component inheritance
|
|
173
|
+
- When generic components are always wrapped by specific ones
|
|
174
|
+
- Clear ownership of heading level decision
|
|
175
|
+
|
|
176
|
+
## Visual-Only Headings
|
|
177
|
+
|
|
178
|
+
When text should look like a heading but not affect document outline:
|
|
179
|
+
|
|
180
|
+
```html
|
|
181
|
+
<!-- Looks like a heading, but isn't one semantically -->
|
|
182
|
+
<p class="u-heading-xl">Sale ends tomorrow!</p>
|
|
183
|
+
|
|
184
|
+
<!-- Compare to actual heading -->
|
|
185
|
+
<h2 class="u-heading-xl">Product Categories</h2>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### When to Use
|
|
189
|
+
|
|
190
|
+
- Promotional text that looks prominent but isn't structural
|
|
191
|
+
- Decorative typography
|
|
192
|
+
- Labels that don't introduce content sections
|
|
193
|
+
|
|
194
|
+
### CSS Utility Classes
|
|
195
|
+
|
|
196
|
+
```css
|
|
197
|
+
/* Size-based utilities separate from semantic level */
|
|
198
|
+
.u-heading-xs {
|
|
199
|
+
font-size: var(--font-size-xs);
|
|
200
|
+
}
|
|
201
|
+
.u-heading-s {
|
|
202
|
+
font-size: var(--font-size-s);
|
|
203
|
+
}
|
|
204
|
+
.u-heading-m {
|
|
205
|
+
font-size: var(--font-size-m);
|
|
206
|
+
}
|
|
207
|
+
.u-heading-l {
|
|
208
|
+
font-size: var(--font-size-l);
|
|
209
|
+
}
|
|
210
|
+
.u-heading-xl {
|
|
211
|
+
font-size: var(--font-size-xl);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/* All share heading-like properties */
|
|
215
|
+
.u-heading-xs,
|
|
216
|
+
.u-heading-s,
|
|
217
|
+
.u-heading-m,
|
|
218
|
+
.u-heading-l,
|
|
219
|
+
.u-heading-xl {
|
|
220
|
+
font-weight: var(--font-weight-bold);
|
|
221
|
+
line-height: var(--line-height-tight);
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Checklist for Component Headings
|
|
226
|
+
|
|
227
|
+
When building a component with a heading:
|
|
228
|
+
|
|
229
|
+
- [ ] Is the heading level configurable?
|
|
230
|
+
- [ ] Is there a sensible default?
|
|
231
|
+
- [ ] Is the default documented?
|
|
232
|
+
- [ ] Can visual appearance be controlled independently?
|
|
233
|
+
- [ ] Does the component work at all reasonable heading levels?
|
|
234
|
+
- [ ] Is the expected context documented?
|
|
235
|
+
|
|
236
|
+
## Common Mistakes
|
|
237
|
+
|
|
238
|
+
### Hardcoded Levels
|
|
239
|
+
|
|
240
|
+
```jsx
|
|
241
|
+
// Fragile: breaks when used outside expected context
|
|
242
|
+
function Card({ title }) {
|
|
243
|
+
return (
|
|
244
|
+
<article>
|
|
245
|
+
<h3>{title}</h3> {/* What if this is used at h2 level? */}
|
|
246
|
+
</article>
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Level Chosen for Appearance
|
|
252
|
+
|
|
253
|
+
```html
|
|
254
|
+
<!-- Wrong: h4 chosen because it's smaller -->
|
|
255
|
+
<h4 class="card-title">Product Name</h4>
|
|
256
|
+
|
|
257
|
+
<!-- Right: appropriate level with visual override -->
|
|
258
|
+
<h3 class="card-title u-heading-s">Product Name</h3>
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Missing Defaults
|
|
262
|
+
|
|
263
|
+
```jsx
|
|
264
|
+
// Dangerous: undefined heading level
|
|
265
|
+
function Card({ title, headingLevel }) {
|
|
266
|
+
const Heading = `h${headingLevel}`; // h undefined if not passed
|
|
267
|
+
return <Heading>{title}</Heading>;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Safe: always has a valid level
|
|
271
|
+
function Card({ title, headingLevel = 3 }) {
|
|
272
|
+
const Heading = `h${headingLevel}`;
|
|
273
|
+
return <Heading>{title}</Heading>;
|
|
274
|
+
}
|
|
275
|
+
```
|