@natjswenson/devlog 0.1.8 → 0.1.9
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 +10 -0
- package/examples/react/DevLogPage.css +23 -15
- package/examples/react/DevLogPage.jsx +27 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@natjswenson/devlog` are documented here.
|
|
4
4
|
|
|
5
|
+
## 0.1.9 (2026-06-05) — accessibility fix
|
|
6
|
+
|
|
7
|
+
**Accessibility**
|
|
8
|
+
- The drop-in React component (`examples/react/DevLogPage.jsx`) now exposes the expand/collapse entries as a proper disclosure control. Previously they were mouse-only — a bare `onClick` on `<article>` with no `role`, `tabIndex`, `aria-expanded`, or keyboard handler, so keyboard and screen-reader users could not operate the feed.
|
|
9
|
+
- The header carries `role="button"`, `tabIndex={0}`, `aria-expanded`, and `aria-controls` for screen-reader toggle semantics.
|
|
10
|
+
- `Enter`/`Space` toggle the focused entry (`preventDefault` on Space stops page scroll).
|
|
11
|
+
- `:focus-visible` outline makes keyboard focus visible.
|
|
12
|
+
- The toggle moved from the whole card to the header, so links inside an expanded entry are no longer nested in an interactive ancestor and text selection in the body works normally.
|
|
13
|
+
- Visuals are unchanged: padding/hover/cursor moved from `.devlog-entry` to `.devlog-header`, with the redundant content padding zeroed so spacing matches.
|
|
14
|
+
|
|
5
15
|
## 0.1.8 (2026-05-01) — final hardening pass
|
|
6
16
|
|
|
7
17
|
Closes the four Low-Hardening findings from the second adversarial verification:
|
|
@@ -75,33 +75,39 @@
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
.devlog-entry {
|
|
78
|
-
padding: 28px 0;
|
|
79
78
|
border-bottom: 1px solid var(--devlog-border);
|
|
80
|
-
cursor: pointer;
|
|
81
|
-
transition: background var(--devlog-transition-fast);
|
|
82
79
|
}
|
|
83
80
|
|
|
84
81
|
.devlog-entry:first-child {
|
|
85
82
|
border-top: 1px solid var(--devlog-border);
|
|
86
83
|
}
|
|
87
84
|
|
|
88
|
-
|
|
89
|
-
background: var(--devlog-bg-surface);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
.devlog-entry--expanded,
|
|
93
|
-
.devlog-entry--expanded:hover {
|
|
94
|
-
cursor: default;
|
|
95
|
-
background: none;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/* ─── Entry header ──────────────────────────────────────────────────── */
|
|
85
|
+
/* ─── Entry header (the disclosure control) ─────────────────────────── */
|
|
99
86
|
|
|
100
87
|
.devlog-header {
|
|
101
88
|
display: flex;
|
|
102
89
|
justify-content: space-between;
|
|
103
90
|
align-items: flex-start;
|
|
104
91
|
gap: 24px;
|
|
92
|
+
padding: 28px 0;
|
|
93
|
+
cursor: pointer;
|
|
94
|
+
transition: background var(--devlog-transition-fast);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.devlog-header:hover {
|
|
98
|
+
background: var(--devlog-bg-surface);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.devlog-header:focus-visible {
|
|
102
|
+
outline: 2px solid var(--devlog-fg);
|
|
103
|
+
outline-offset: -2px;
|
|
104
|
+
border-radius: 4px;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.devlog-entry--expanded .devlog-header,
|
|
108
|
+
.devlog-entry--expanded .devlog-header:hover {
|
|
109
|
+
cursor: default;
|
|
110
|
+
background: none;
|
|
105
111
|
}
|
|
106
112
|
|
|
107
113
|
.devlog-header__left {
|
|
@@ -163,8 +169,10 @@
|
|
|
163
169
|
overflow: hidden;
|
|
164
170
|
}
|
|
165
171
|
|
|
172
|
+
/* Header already contributes 28px of bottom padding above this region,
|
|
173
|
+
so the expanded body sits flush against it. */
|
|
166
174
|
.devlog-content {
|
|
167
|
-
padding-top:
|
|
175
|
+
padding-top: 0;
|
|
168
176
|
}
|
|
169
177
|
|
|
170
178
|
.devlog-content h2 {
|
|
@@ -81,6 +81,15 @@ export default function DevLogPage({
|
|
|
81
81
|
}
|
|
82
82
|
}, [expandedEntry, loadedContent, fetchEntryContent]);
|
|
83
83
|
|
|
84
|
+
// Enter/Space toggle the focused entry — keyboard parity with the click
|
|
85
|
+
// handler. preventDefault on Space stops the page from scrolling.
|
|
86
|
+
const handleKeyDown = useCallback((e, filename) => {
|
|
87
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
88
|
+
e.preventDefault();
|
|
89
|
+
handleToggle(filename);
|
|
90
|
+
}
|
|
91
|
+
}, [handleToggle]);
|
|
92
|
+
|
|
84
93
|
const visibleEntries = entries.slice(0, visibleCount);
|
|
85
94
|
const hasMore = visibleCount < entries.length;
|
|
86
95
|
const showTabs = projects && projects.length > 1;
|
|
@@ -132,14 +141,28 @@ export default function DevLogPage({
|
|
|
132
141
|
{visibleEntries.map((entry) => {
|
|
133
142
|
const isExpanded = expandedEntry === entry.file;
|
|
134
143
|
const content = loadedContent.get(entry.file);
|
|
144
|
+
const contentId = `devlog-content-${entry.file}`;
|
|
135
145
|
|
|
136
146
|
return (
|
|
137
147
|
<article
|
|
138
148
|
key={entry.file}
|
|
139
149
|
className={`devlog-entry${isExpanded ? ' devlog-entry--expanded' : ''}`}
|
|
140
|
-
onClick={() => handleToggle(entry.file)}
|
|
141
150
|
>
|
|
142
|
-
|
|
151
|
+
{/* The header is the disclosure control: role=button +
|
|
152
|
+
aria-expanded/aria-controls give screen readers the
|
|
153
|
+
toggle semantics, and it's keyboard-focusable. Keeping
|
|
154
|
+
it separate from the content region (rather than wrapping
|
|
155
|
+
the whole card in onClick) means links inside an expanded
|
|
156
|
+
entry aren't trapped inside an interactive ancestor. */}
|
|
157
|
+
<div
|
|
158
|
+
className="devlog-header"
|
|
159
|
+
role="button"
|
|
160
|
+
tabIndex={0}
|
|
161
|
+
aria-expanded={isExpanded}
|
|
162
|
+
aria-controls={contentId}
|
|
163
|
+
onClick={() => handleToggle(entry.file)}
|
|
164
|
+
onKeyDown={(e) => handleKeyDown(e, entry.file)}
|
|
165
|
+
>
|
|
143
166
|
<div className="devlog-header__left">
|
|
144
167
|
<p className="devlog-date">{formatDate(entry.date)}</p>
|
|
145
168
|
<h2 className="devlog-title">{entry.title}</h2>
|
|
@@ -150,10 +173,10 @@ export default function DevLogPage({
|
|
|
150
173
|
</div>
|
|
151
174
|
</div>
|
|
152
175
|
|
|
153
|
-
<div className="devlog-content-wrapper">
|
|
176
|
+
<div className="devlog-content-wrapper" id={contentId}>
|
|
154
177
|
<div className="devlog-content-inner">
|
|
155
178
|
{isExpanded && content && (
|
|
156
|
-
<div className="devlog-content"
|
|
179
|
+
<div className="devlog-content">
|
|
157
180
|
<ReactMarkdown
|
|
158
181
|
remarkPlugins={[remarkGfm]}
|
|
159
182
|
urlTransform={safeUrlTransform}
|
package/package.json
CHANGED