@hraness/design-kit 0.35.1 → 0.35.2
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/ARTICLE_COPY.md +7 -1
- package/README.md +4 -2
- package/dist/{chunk-6bfr0g23.js → chunk-4tgt8egt.js} +17 -12
- package/dist/{chunk-c80g58j1.js → chunk-fmeg5cxf.js} +14 -6
- package/dist/index.js +1 -1
- package/dist/react/index.js +4 -2
- package/dist/react/server.js +1 -1
- package/dist/stylex-manifest.json +1 -1
- package/package.json +1 -1
- package/portfolio-inventory.json +1 -1
- package/src/article-html.ts +41 -19
- package/src/react/article.tsx +32 -14
- package/src/react/design-gallery.tsx +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hraness/design-kit",
|
|
3
|
-
"version": "0.35.
|
|
3
|
+
"version": "0.35.2",
|
|
4
4
|
"description": "React components and CSS for Hraness apps and product sites: application shells, marketing sections, charts, themes, effects, and syntax highlighting. Built on @hraness/ui.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/portfolio-inventory.json
CHANGED
package/src/article-html.ts
CHANGED
|
@@ -83,6 +83,8 @@ export interface ArticleHtmlInput {
|
|
|
83
83
|
readonly id?: string;
|
|
84
84
|
readonly provenance: ArticleProvenanceRecord | null;
|
|
85
85
|
readonly published: ArticleIsoDate;
|
|
86
|
+
/** Show publication and update dates. Defaults to true; dates remain required and validated when hidden. */
|
|
87
|
+
readonly showDates?: boolean;
|
|
86
88
|
readonly toc?: readonly ArticleTocItem[];
|
|
87
89
|
readonly tocLabel?: string;
|
|
88
90
|
readonly updated?: ArticleIsoDate;
|
|
@@ -91,7 +93,7 @@ export interface ArticleHtmlInput {
|
|
|
91
93
|
/** The static equivalent of `MarketingArticle`. */
|
|
92
94
|
export function renderArticleHtml(input: ArticleHtmlInput): string {
|
|
93
95
|
const headingId = input.headingId ?? "article-title";
|
|
94
|
-
const { published, updated } = input;
|
|
96
|
+
const { published, updated, showDates = true } = input;
|
|
95
97
|
assertArticleDates(updated === undefined ? { published } : { published, updated });
|
|
96
98
|
const toc = input.toc ?? [];
|
|
97
99
|
for (const item of toc) {
|
|
@@ -99,16 +101,16 @@ export function renderArticleHtml(input: ArticleHtmlInput): string {
|
|
|
99
101
|
}
|
|
100
102
|
const tocId = `${headingId}-contents`;
|
|
101
103
|
const meta = [
|
|
102
|
-
input.author === undefined ? "" : renderArticleBylineHtml(input.author)
|
|
103
|
-
dateHtml("Published", published),
|
|
104
|
-
updated
|
|
105
|
-
].join(
|
|
104
|
+
input.author === undefined ? "" : renderArticleBylineHtml(input.author),
|
|
105
|
+
showDates ? dateHtml("Published", published) : "",
|
|
106
|
+
showDates && updated !== undefined ? dateHtml("Updated", updated) : "",
|
|
107
|
+
].filter(Boolean).join(SEPARATOR);
|
|
106
108
|
const header = [
|
|
107
109
|
'<header class="plain-publication__article-header">',
|
|
108
110
|
present(input.eyebrow) ? `<p class="plain-publication__eyebrow">${escapeArticleHtml(input.eyebrow)}</p>` : "",
|
|
109
111
|
`<h1 id="${escapeArticleHtml(headingId)}">${escapeArticleHtml(input.heading)}</h1>`,
|
|
110
112
|
present(input.dek) ? `<p class="plain-publication__article-dek">${escapeArticleHtml(input.dek)}</p>` : "",
|
|
111
|
-
`<p class="plain-publication__article-meta">${meta}</p>`,
|
|
113
|
+
meta === "" ? "" : `<p class="plain-publication__article-meta">${meta}</p>`,
|
|
112
114
|
input.provenance === null ? "" : renderArticleProvenanceHtml(input.provenance),
|
|
113
115
|
"</header>",
|
|
114
116
|
].join("");
|
|
@@ -135,21 +137,35 @@ export function renderArticleHtml(input: ArticleHtmlInput): string {
|
|
|
135
137
|
export function renderArticleSourcesHtml({
|
|
136
138
|
heading = ARTICLE_SOURCES_HEADING,
|
|
137
139
|
headingId = "article-sources",
|
|
140
|
+
showDates = true,
|
|
138
141
|
sources,
|
|
139
|
-
}: Readonly<{
|
|
142
|
+
}: Readonly<{
|
|
143
|
+
heading?: string;
|
|
144
|
+
headingId?: string;
|
|
145
|
+
/** Show check dates. Defaults to true; checkedOn remains required and validated when hidden. */
|
|
146
|
+
showDates?: boolean;
|
|
147
|
+
sources: readonly ArticleSourceItem[];
|
|
148
|
+
}>): string {
|
|
140
149
|
if (sources.length === 0) return "";
|
|
141
|
-
for (const source of sources)
|
|
150
|
+
for (const source of sources) {
|
|
151
|
+
assertArticleHref(source.href);
|
|
152
|
+
formatArticleDate(source.checkedOn);
|
|
153
|
+
}
|
|
142
154
|
return [
|
|
143
155
|
`<section aria-labelledby="${escapeArticleHtml(headingId)}" class="plain-publication__sources">`,
|
|
144
156
|
`<h2 id="${escapeArticleHtml(headingId)}">${escapeArticleHtml(heading)}</h2>`,
|
|
145
157
|
"<ol>",
|
|
146
|
-
...sources.map((source) =>
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
158
|
+
...sources.map((source) => {
|
|
159
|
+
const meta = [
|
|
160
|
+
present(source.publisher) ? escapeArticleHtml(source.publisher) : "",
|
|
161
|
+
showDates ? dateHtml("Checked", source.checkedOn) : "",
|
|
162
|
+
].filter(Boolean).join(SEPARATOR);
|
|
163
|
+
return [
|
|
164
|
+
`<li><a href="${escapeArticleHtml(source.href)}">${escapeArticleHtml(source.title)}</a>`,
|
|
165
|
+
meta === "" ? "" : `<span>${meta}</span>`,
|
|
166
|
+
"</li>",
|
|
167
|
+
].join("");
|
|
168
|
+
}),
|
|
153
169
|
"</ol></section>",
|
|
154
170
|
].join("");
|
|
155
171
|
}
|
|
@@ -228,6 +244,7 @@ export function renderArticleIndexHtml({
|
|
|
228
244
|
headingLevel = 2,
|
|
229
245
|
id,
|
|
230
246
|
items,
|
|
247
|
+
showDates = true,
|
|
231
248
|
summary,
|
|
232
249
|
}: Readonly<{
|
|
233
250
|
className?: string;
|
|
@@ -236,6 +253,8 @@ export function renderArticleIndexHtml({
|
|
|
236
253
|
headingLevel?: 1 | 2 | 3 | 4 | 5;
|
|
237
254
|
id?: string;
|
|
238
255
|
items: readonly ArticleIndexItem[];
|
|
256
|
+
/** Show publication and update dates. Defaults to true; item dates remain required and validated when hidden. */
|
|
257
|
+
showDates?: boolean;
|
|
239
258
|
summary?: string;
|
|
240
259
|
}>): string {
|
|
241
260
|
if (![1, 2, 3, 4, 5].includes(headingLevel)) throw new RangeError("Article index heading level must be 1 to 5.");
|
|
@@ -259,10 +278,13 @@ export function renderArticleIndexHtml({
|
|
|
259
278
|
present(item.eyebrow) ? `<p class="plain-publication__entry-label">${escapeArticleHtml(item.eyebrow)}</p>` : "",
|
|
260
279
|
`<${entry} class="plain-publication__entry-title"><a href="${escapeArticleHtml(item.href)}">${escapeArticleHtml(item.title)}</a></${entry}>`,
|
|
261
280
|
`<p class="plain-publication__entry-dek">${escapeArticleHtml(item.dek)}</p>`,
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
281
|
+
showDates ? [
|
|
282
|
+
'<p class="plain-publication__entry-meta">',
|
|
283
|
+
dateHtml("Published", item.published),
|
|
284
|
+
item.updated === undefined ? "" : SEPARATOR + dateHtml("Updated", item.updated),
|
|
285
|
+
"</p>",
|
|
286
|
+
].join("") : "",
|
|
287
|
+
"</article>",
|
|
266
288
|
].join("")),
|
|
267
289
|
"</div></section>",
|
|
268
290
|
].join("");
|
package/src/react/article.tsx
CHANGED
|
@@ -93,6 +93,8 @@ export interface MarketingArticleProps {
|
|
|
93
93
|
/** Required so every article decides its drafting note. Pass null only for writing with no AI involvement and no review record. */
|
|
94
94
|
readonly provenance: ArticleProvenanceRecord | null;
|
|
95
95
|
readonly published: ArticleIsoDate;
|
|
96
|
+
/** Show publication and update dates. Defaults to true; dates remain required and validated when hidden. */
|
|
97
|
+
readonly showDates?: boolean;
|
|
96
98
|
readonly toc?: readonly ArticleTocItem[];
|
|
97
99
|
readonly tocLabel?: string;
|
|
98
100
|
readonly updated?: ArticleIsoDate;
|
|
@@ -116,6 +118,7 @@ export function MarketingArticle({
|
|
|
116
118
|
id,
|
|
117
119
|
provenance,
|
|
118
120
|
published,
|
|
121
|
+
showDates = true,
|
|
119
122
|
toc,
|
|
120
123
|
tocLabel = ARTICLE_TOC_LABEL,
|
|
121
124
|
updated,
|
|
@@ -138,11 +141,13 @@ export function MarketingArticle({
|
|
|
138
141
|
{eyebrow === undefined || eyebrow === "" ? null : <p className="plain-publication__eyebrow">{eyebrow}</p>}
|
|
139
142
|
<h1 id={headingId}>{heading}</h1>
|
|
140
143
|
{dek === undefined || dek === "" ? null : <p className="plain-publication__article-dek">{dek}</p>}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
{author === undefined && !showDates ? null : (
|
|
145
|
+
<p className="plain-publication__article-meta">
|
|
146
|
+
{author === undefined ? null : <><ArticleByline author={author} />{showDates ? <Separator /> : null}</>}
|
|
147
|
+
{showDates ? <ArticleDate label="Published" value={published} /> : null}
|
|
148
|
+
{!showDates || updated === undefined ? null : <><Separator /><ArticleDate label="Updated" value={updated} /></>}
|
|
149
|
+
</p>
|
|
150
|
+
)}
|
|
146
151
|
{provenance === null ? null : <ArticleProvenance provenance={provenance} />}
|
|
147
152
|
</header>
|
|
148
153
|
<div className="plain-publication__article-layout">
|
|
@@ -165,14 +170,20 @@ export function MarketingArticle({
|
|
|
165
170
|
export function ArticleSources({
|
|
166
171
|
heading = ARTICLE_SOURCES_HEADING,
|
|
167
172
|
headingId = "article-sources",
|
|
173
|
+
showDates = true,
|
|
168
174
|
sources,
|
|
169
175
|
}: Readonly<{
|
|
170
176
|
heading?: string;
|
|
171
177
|
headingId?: string;
|
|
178
|
+
/** Show check dates. Defaults to true; checkedOn remains required and validated when hidden. */
|
|
179
|
+
showDates?: boolean;
|
|
172
180
|
sources: readonly ArticleSourceItem[];
|
|
173
181
|
}>) {
|
|
174
182
|
if (sources.length === 0) return null;
|
|
175
|
-
for (const source of sources)
|
|
183
|
+
for (const source of sources) {
|
|
184
|
+
assertArticleHref(source.href);
|
|
185
|
+
formatArticleDate(source.checkedOn);
|
|
186
|
+
}
|
|
176
187
|
return (
|
|
177
188
|
<section aria-labelledby={headingId} className="plain-publication__sources">
|
|
178
189
|
<h2 id={headingId}>{heading}</h2>
|
|
@@ -180,10 +191,12 @@ export function ArticleSources({
|
|
|
180
191
|
{sources.map((source) => (
|
|
181
192
|
<li key={`${source.href}-${source.title}`}>
|
|
182
193
|
<a href={source.href}>{source.title}</a>
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
194
|
+
{!showDates && (source.publisher === undefined || source.publisher === "") ? null : (
|
|
195
|
+
<span>
|
|
196
|
+
{source.publisher === undefined || source.publisher === "" ? null : <>{source.publisher}{showDates ? <Separator /> : null}</>}
|
|
197
|
+
{showDates ? <ArticleDate label="Checked" value={source.checkedOn} /> : null}
|
|
198
|
+
</span>
|
|
199
|
+
)}
|
|
187
200
|
</li>
|
|
188
201
|
))}
|
|
189
202
|
</ol>
|
|
@@ -253,6 +266,7 @@ export function ArticleIndex({
|
|
|
253
266
|
headingLevel = 2,
|
|
254
267
|
id,
|
|
255
268
|
items,
|
|
269
|
+
showDates = true,
|
|
256
270
|
summary,
|
|
257
271
|
}: Readonly<{
|
|
258
272
|
className?: string;
|
|
@@ -261,6 +275,8 @@ export function ArticleIndex({
|
|
|
261
275
|
headingLevel?: 1 | 2 | 3 | 4 | 5;
|
|
262
276
|
id?: string;
|
|
263
277
|
items: readonly ArticleIndexItem[];
|
|
278
|
+
/** Show publication and update dates. Defaults to true; item dates remain required and validated when hidden. */
|
|
279
|
+
showDates?: boolean;
|
|
264
280
|
summary?: string;
|
|
265
281
|
}>) {
|
|
266
282
|
if (![1, 2, 3, 4, 5].includes(headingLevel)) throw new RangeError("Article index heading level must be 1 to 5.");
|
|
@@ -290,10 +306,12 @@ export function ArticleIndex({
|
|
|
290
306
|
{item.eyebrow === undefined || item.eyebrow === "" ? null : <p className="plain-publication__entry-label">{item.eyebrow}</p>}
|
|
291
307
|
<EntryTag className="plain-publication__entry-title"><a href={item.href}>{item.title}</a></EntryTag>
|
|
292
308
|
<p className="plain-publication__entry-dek">{item.dek}</p>
|
|
293
|
-
|
|
294
|
-
<
|
|
295
|
-
|
|
296
|
-
|
|
309
|
+
{showDates ? (
|
|
310
|
+
<p className="plain-publication__entry-meta">
|
|
311
|
+
<ArticleDate label="Published" value={item.published} />
|
|
312
|
+
{item.updated === undefined ? null : <><Separator /><ArticleDate label="Updated" value={item.updated} /></>}
|
|
313
|
+
</p>
|
|
314
|
+
) : null}
|
|
297
315
|
</article>
|
|
298
316
|
))}
|
|
299
317
|
</div>
|
|
@@ -678,6 +678,7 @@ export function DesignSystemGallery({
|
|
|
678
678
|
after={(
|
|
679
679
|
<ArticleSources
|
|
680
680
|
headingId="gallery-article-sources"
|
|
681
|
+
showDates={false}
|
|
681
682
|
sources={[
|
|
682
683
|
{ checkedOn: "2026-09-20", href: "#articles", publisher: "Relay", title: "Relay 2.4 release notes" },
|
|
683
684
|
]}
|
|
@@ -693,6 +694,7 @@ export function DesignSystemGallery({
|
|
|
693
694
|
review: { reviewer: "an independent AI editorial review", reviewerType: "ai" },
|
|
694
695
|
}}
|
|
695
696
|
published="2026-09-10"
|
|
697
|
+
showDates={false}
|
|
696
698
|
toc={[
|
|
697
699
|
{ href: "#gallery-article-problem", label: "The problem" },
|
|
698
700
|
{ href: "#gallery-article-approach", label: "The approach" },
|