@devslab/site-kit 0.5.2 → 0.6.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/README.ko.md +24 -0
- package/README.md +24 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/layouts.d.ts +3 -0
- package/dist/layouts.d.ts.map +1 -1
- package/dist/sections.d.ts +70 -0
- package/dist/sections.d.ts.map +1 -0
- package/dist/solid.js +227 -120
- package/dist/solid.js.map +1 -1
- package/dist/solid.server.js +174 -41
- package/dist/solid.server.js.map +1 -1
- package/package.json +7 -4
- package/site-sections.css +65 -0
- package/src/core/locales.d.mts +2 -0
- package/src/core/locales.mjs +20 -5
- package/styles.css +2 -0
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devslab/site-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Strict i18n, SEO/GEO, and Solid public-site shells for DevsLab products",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
7
7
|
"sideEffects": [
|
|
8
|
-
"./styles.css"
|
|
8
|
+
"./styles.css",
|
|
9
|
+
"./site-sections.css"
|
|
9
10
|
],
|
|
10
11
|
"repository": {
|
|
11
12
|
"type": "git",
|
|
@@ -24,6 +25,7 @@
|
|
|
24
25
|
"src/tanstack-start.mjs",
|
|
25
26
|
"src/tanstack-start.d.mts",
|
|
26
27
|
"styles.css",
|
|
28
|
+
"site-sections.css",
|
|
27
29
|
"README.md",
|
|
28
30
|
"README.ko.md"
|
|
29
31
|
],
|
|
@@ -50,10 +52,11 @@
|
|
|
50
52
|
"types": "./src/tanstack-start.d.mts",
|
|
51
53
|
"import": "./src/tanstack-start.mjs"
|
|
52
54
|
},
|
|
53
|
-
"./styles.css": "./styles.css"
|
|
55
|
+
"./styles.css": "./styles.css",
|
|
56
|
+
"./site-sections.css": "./site-sections.css"
|
|
54
57
|
},
|
|
55
58
|
"dependencies": {
|
|
56
|
-
"@devslab/dds-solid": "0.
|
|
59
|
+
"@devslab/dds-solid": "0.6.0"
|
|
57
60
|
},
|
|
58
61
|
"peerDependencies": {
|
|
59
62
|
"@tanstack/solid-router": "1.170.30",
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/* Section primitives (sections.tsx). Every selector here is prefixed
|
|
2
|
+
site-section / site-hero / site-steps / site-rows / site-pricing and is
|
|
3
|
+
defined once. Tokens only; no colour literals; no theme rules — the
|
|
4
|
+
tokens carry light and dark. */
|
|
5
|
+
|
|
6
|
+
.site-section { padding-block: var(--dds-space-64); border-block-start: 1px solid var(--dds-color-border-default); }
|
|
7
|
+
.site-section[data-tone="band"] { background: var(--dds-color-bg-subtle); }
|
|
8
|
+
.site-section__shell { inline-size: min(100% - var(--dds-space-32), 1120px); margin-inline: auto; }
|
|
9
|
+
|
|
10
|
+
.site-section__head { max-inline-size: 46rem; margin-block-end: var(--dds-space-40); }
|
|
11
|
+
.site-section__index { margin: 0; margin-block-end: var(--dds-space-12); color: var(--dds-color-text-brand); font-family: var(--dds-font-family-mono); font-size: var(--dds-typo-caption-font-size); letter-spacing: .18em; }
|
|
12
|
+
.site-section__head h2 { margin: 0; font-size: var(--dds-typo-title-1-font-size); line-height: var(--dds-typo-title-1-line-height); letter-spacing: -.02em; text-wrap: balance; white-space: pre-line; }
|
|
13
|
+
.site-section__lede { margin: 0; margin-block-start: var(--dds-space-12); max-inline-size: 44rem; color: var(--dds-color-text-secondary); font-size: var(--dds-typo-body-1-font-size); line-height: 1.7; }
|
|
14
|
+
|
|
15
|
+
@media (max-width: 48rem) {
|
|
16
|
+
.site-section { padding-block: var(--dds-space-48); }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
html:lang(ko) .site-section__head h2,
|
|
20
|
+
html:lang(ko) .site-section__lede { word-break: keep-all; overflow-wrap: break-word; }
|
|
21
|
+
|
|
22
|
+
/* Steps: three columns sharing one hairline; ring numerals in the body face. */
|
|
23
|
+
.site-steps { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0; margin: 0; padding: 0; list-style: none; border-block-start: 1px solid var(--dds-color-border-default); }
|
|
24
|
+
.site-steps__step { padding-block: var(--dds-space-24); padding-inline-end: var(--dds-space-24); border-block-end: 1px solid var(--dds-color-border-default); }
|
|
25
|
+
.site-steps__marker { display: inline-flex; align-items: center; justify-content: center; inline-size: 28px; block-size: 28px; border-radius: var(--dds-radius-full); border: 1.5px solid var(--dds-color-text-brand); color: var(--dds-color-text-brand); font-family: var(--dds-font-family-sans); font-size: 13px; font-weight: 600; line-height: 1; }
|
|
26
|
+
.site-steps__step h3 { margin-block: var(--dds-space-12) var(--dds-space-8); font-size: var(--dds-typo-title-3-font-size); }
|
|
27
|
+
.site-steps__step p { margin: 0; color: var(--dds-color-text-secondary); font-size: var(--dds-typo-body-2-font-size); line-height: 1.7; }
|
|
28
|
+
@media (min-width: 56.25rem) {
|
|
29
|
+
.site-steps { grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr)); }
|
|
30
|
+
.site-steps__step + .site-steps__step { padding-inline-start: var(--dds-space-24); border-inline-start: 1px solid var(--dds-color-border-default); }
|
|
31
|
+
}
|
|
32
|
+
html:lang(ko) .site-steps__step p { word-break: keep-all; overflow-wrap: break-word; }
|
|
33
|
+
|
|
34
|
+
/* Rows: title column + body column above the two-column threshold, stacked below. */
|
|
35
|
+
.site-rows { margin: 0; padding: 0; list-style: none; border-block-start: 1px solid var(--dds-color-border-default); }
|
|
36
|
+
.site-rows__row { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--dds-space-8) var(--dds-space-24); padding-block: var(--dds-space-24); border-block-end: 1px solid var(--dds-color-border-default); }
|
|
37
|
+
.site-rows__title { display: flex; flex-wrap: wrap; align-items: center; gap: var(--dds-space-8); }
|
|
38
|
+
.site-rows__title h3 { margin: 0; font-size: var(--dds-typo-title-3-font-size); }
|
|
39
|
+
.site-rows__body { margin: 0; max-inline-size: 52rem; color: var(--dds-color-text-secondary); font-size: var(--dds-typo-body-2-font-size); line-height: 1.7; }
|
|
40
|
+
@media (min-width: 56.25rem) {
|
|
41
|
+
.site-rows__row { grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); }
|
|
42
|
+
}
|
|
43
|
+
html:lang(ko) .site-rows__body { word-break: keep-all; overflow-wrap: break-word; }
|
|
44
|
+
|
|
45
|
+
/* Pricing note: a paragraph block and one action, side by side when there is room. */
|
|
46
|
+
.site-pricing { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: var(--dds-space-24); }
|
|
47
|
+
.site-pricing__note { flex: 1 1 28rem; max-inline-size: 60ch; }
|
|
48
|
+
.site-pricing__note p { margin: 0; color: var(--dds-color-text-secondary); line-height: 1.7; }
|
|
49
|
+
.site-pricing__note p + p { margin-block-start: var(--dds-space-8); }
|
|
50
|
+
.site-pricing__action { flex: 0 0 auto; }
|
|
51
|
+
|
|
52
|
+
/* Hero: copy 6 / aside 5, one column below the threshold. The aside has no
|
|
53
|
+
height rule — a swapping scene fixes its own height in the consumer. */
|
|
54
|
+
.site-hero { padding-block: var(--dds-space-64); }
|
|
55
|
+
.site-hero__shell { inline-size: min(100% - var(--dds-space-32), 1120px); margin-inline: auto; display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--dds-space-48); align-items: start; }
|
|
56
|
+
.site-hero__eyebrow { margin: 0; margin-block-end: var(--dds-space-12); color: var(--dds-color-text-brand); font-family: var(--dds-font-family-mono); font-size: var(--dds-typo-caption-font-size); letter-spacing: .18em; }
|
|
57
|
+
.site-hero__copy h1 { margin: 0; font-size: var(--dds-typo-display-font-size); line-height: var(--dds-typo-display-line-height); letter-spacing: -.025em; text-wrap: balance; white-space: pre-line; }
|
|
58
|
+
.site-hero__lede { margin: 0; margin-block-start: var(--dds-space-24); max-inline-size: 46ch; color: var(--dds-color-text-secondary); font-size: var(--dds-typo-body-1-font-size); line-height: 1.7; }
|
|
59
|
+
.site-hero__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--dds-space-16) var(--dds-space-24); margin-block-start: var(--dds-space-32); }
|
|
60
|
+
.site-hero__aside { margin: 0; inline-size: 100%; }
|
|
61
|
+
@media (min-width: 56.25rem) {
|
|
62
|
+
.site-hero__shell { grid-template-columns: 6fr 5fr; }
|
|
63
|
+
}
|
|
64
|
+
html:lang(ko) .site-hero__copy h1,
|
|
65
|
+
html:lang(ko) .site-hero__lede { word-break: keep-all; overflow-wrap: break-word; }
|
package/src/core/locales.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export type SiteLocale = "ko" | "en" | "ja" | "zh-HK" | "zh-TW" | "hi" | "vi" | "id" | "th" | "pt-BR" | "fr" | "de" | "es" | "ar";
|
|
2
|
+
export type LocaleCode = SiteLocale;
|
|
2
3
|
export type TextDirection = "ltr" | "rtl";
|
|
3
4
|
export interface LocaleDefinition { readonly code: SiteLocale; readonly language: string; readonly nativeName: string; readonly dir: TextDirection }
|
|
4
5
|
|
|
@@ -31,6 +32,7 @@ export declare const LOCALES: readonly LocaleDefinition[];
|
|
|
31
32
|
* are the family registry, so not calling this changes nothing.
|
|
32
33
|
*/
|
|
33
34
|
export declare function defineLocaleRegistry<Extra extends string = never>(options?: {
|
|
35
|
+
only?: ReadonlyArray<LocaleCode>;
|
|
34
36
|
extra?: readonly ExtraLocaleDefinition<Extra>[];
|
|
35
37
|
aliases?: readonly (readonly [string, SiteLocale | Extra])[];
|
|
36
38
|
}): LocaleRegistry<SiteLocale | Extra>;
|
package/src/core/locales.mjs
CHANGED
|
@@ -41,22 +41,37 @@ const FAMILY_ALIASES = Object.freeze([["zh", "zh-TW"], ["pt", "pt-BR"]]);
|
|
|
41
41
|
* and security-scanned here, and seven Indian languages share one flag
|
|
42
42
|
* anyway.
|
|
43
43
|
*
|
|
44
|
-
* @param {{ extra?: ReadonlyArray<object>, aliases?: ReadonlyArray<[string, string]> }} [options]
|
|
44
|
+
* @param {{ only?: ReadonlyArray<string>, extra?: ReadonlyArray<object>, aliases?: ReadonlyArray<[string, string]> }} [options]
|
|
45
45
|
*/
|
|
46
46
|
export function defineLocaleRegistry(options = {}) {
|
|
47
47
|
const extra = options.extra ?? [];
|
|
48
|
-
|
|
48
|
+
// `only` — the product sells in a subset of the family's languages. The
|
|
49
|
+
// subset keeps the family's order (the picker order is a family decision)
|
|
50
|
+
// and is applied before `extra`, so a product may still add its own.
|
|
51
|
+
let family = LOCALES;
|
|
52
|
+
if (options.only !== undefined) {
|
|
53
|
+
if (options.only.length === 0) throw new RangeError("`only` needs at least one locale — a registry with none is a bug, not a choice");
|
|
54
|
+
const wanted = new Set(options.only);
|
|
55
|
+
for (const code of wanted) {
|
|
56
|
+
if (!LOCALES.some((locale) => locale.code === code)) throw new RangeError(`${code} is not a family locale — \`only\` selects from the family list; use \`extra\` for a product's own`);
|
|
57
|
+
}
|
|
58
|
+
family = Object.freeze(LOCALES.filter((locale) => wanted.has(locale.code)));
|
|
59
|
+
}
|
|
60
|
+
const seen = new Set(family.map(({ code }) => code));
|
|
49
61
|
for (const locale of extra) {
|
|
50
62
|
if (!locale?.code) throw new TypeError("every extra locale needs a code");
|
|
51
|
-
if (
|
|
63
|
+
if (LOCALES.some((known) => known.code === locale.code)) {
|
|
52
64
|
throw new RangeError(`${locale.code} is already a family locale — remove it from \`extra\``);
|
|
53
65
|
}
|
|
66
|
+
if (seen.has(locale.code)) {
|
|
67
|
+
throw new RangeError(`${locale.code} appears twice in \`extra\``);
|
|
68
|
+
}
|
|
54
69
|
if (!locale.nativeName) throw new TypeError(`${locale.code} needs a nativeName — it is what the picker shows`);
|
|
55
70
|
if (locale.dir !== "ltr" && locale.dir !== "rtl") throw new RangeError(`${locale.code} needs dir "ltr" or "rtl"`);
|
|
56
71
|
seen.add(locale.code);
|
|
57
72
|
}
|
|
58
73
|
|
|
59
|
-
const locales = Object.freeze([...
|
|
74
|
+
const locales = Object.freeze([...family, ...extra.map((locale) => Object.freeze({ ...locale }))]);
|
|
60
75
|
const byLower = new Map(locales.map((locale) => [locale.code.toLowerCase(), locale]));
|
|
61
76
|
const aliases = new Map([...FAMILY_ALIASES, ...(options.aliases ?? [])]);
|
|
62
77
|
|
|
@@ -65,7 +80,7 @@ export function defineLocaleRegistry(options = {}) {
|
|
|
65
80
|
const exact = byLower.get(String(candidate).trim().toLowerCase());
|
|
66
81
|
if (exact) return exact.code;
|
|
67
82
|
const base = String(candidate).trim().toLowerCase().split("-")[0];
|
|
68
|
-
if (aliases.has(base))
|
|
83
|
+
if (aliases.has(base)) { const target = aliases.get(base); return byLower.has(target.toLowerCase()) ? target : undefined; }
|
|
69
84
|
return byLower.get(base)?.code;
|
|
70
85
|
}
|
|
71
86
|
|
package/styles.css
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
@import "@devslab/dds-solid/styles.css";
|
|
2
|
+
@import "./site-sections.css";
|
|
2
3
|
|
|
3
4
|
.dds-sr-only { position: absolute; inline-size: 1px; block-size: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
|
|
4
5
|
.dds-sr-only:focus { position: fixed; inset-block-start: var(--dds-space-8); inset-inline-start: var(--dds-space-8); z-index: 1000; inline-size: auto; block-size: auto; padding: var(--dds-space-8) var(--dds-space-12); margin: 0; overflow: visible; clip-path: none; white-space: normal; border: 2px solid currentColor; border-radius: var(--dds-radius-sm); background: var(--dds-color-bg-default); color: var(--dds-color-text-primary); }
|
|
5
6
|
|
|
6
7
|
.site-shell { min-block-size: 100dvh; display: flex; flex-direction: column; background: var(--dds-color-bg-default); color: var(--dds-color-text-primary); }
|
|
7
8
|
.site-main { flex: 1; inline-size: min(100% - var(--dds-space-32), 1120px); margin-inline: auto; }
|
|
9
|
+
.site-main--bleed { inline-size: 100%; }
|
|
8
10
|
.site-header { position: sticky; inset-block-start: 0; z-index: 100; border-block-end: 1px solid var(--dds-color-border-default); background: var(--dds-color-bg-default); }
|
|
9
11
|
.site-header__inner { min-block-size: 64px; inline-size: min(100% - var(--dds-space-32), 1120px); margin-inline: auto; display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; gap: var(--dds-space-16); }
|
|
10
12
|
.site-brand { justify-self: start; color: var(--dds-color-text-primary); font-weight: 700; text-decoration: none; }
|