@devslab/site-kit 0.14.1 → 0.16.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.
Files changed (130) hide show
  1. package/README.ko.md +266 -3
  2. package/README.md +266 -3
  3. package/dist/chrome.d.ts +10 -0
  4. package/dist/chrome.d.ts.map +1 -1
  5. package/dist/consent.d.ts +36 -0
  6. package/dist/consent.d.ts.map +1 -0
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/solid.js +336 -135
  10. package/dist/solid.js.map +1 -1
  11. package/dist/solid.server.js +282 -25
  12. package/dist/solid.server.js.map +1 -1
  13. package/fonts/geist/LICENSE.txt +93 -0
  14. package/fonts/geist/geist-cyrillic-ext-wght-normal.woff2 +0 -0
  15. package/fonts/geist/geist-cyrillic-wght-normal.woff2 +0 -0
  16. package/fonts/geist/geist-latin-ext-wght-normal.woff2 +0 -0
  17. package/fonts/geist/geist-latin-wght-normal.woff2 +0 -0
  18. package/fonts/geist/geist-vietnamese-wght-normal.woff2 +0 -0
  19. package/fonts/geist-mono/LICENSE.txt +93 -0
  20. package/fonts/geist-mono/geist-mono-cyrillic-ext-wght-normal.woff2 +0 -0
  21. package/fonts/geist-mono/geist-mono-cyrillic-wght-normal.woff2 +0 -0
  22. package/fonts/geist-mono/geist-mono-latin-ext-wght-normal.woff2 +0 -0
  23. package/fonts/geist-mono/geist-mono-latin-wght-normal.woff2 +0 -0
  24. package/fonts/geist-mono/geist-mono-symbols2-wght-normal.woff2 +0 -0
  25. package/fonts/geist-mono/geist-mono-vietnamese-wght-normal.woff2 +0 -0
  26. package/fonts/manifest.json +674 -0
  27. package/fonts/pretendard/LICENSE.txt +94 -0
  28. package/fonts/pretendard/PretendardVariable.subset.0.woff2 +0 -0
  29. package/fonts/pretendard/PretendardVariable.subset.1.woff2 +0 -0
  30. package/fonts/pretendard/PretendardVariable.subset.10.woff2 +0 -0
  31. package/fonts/pretendard/PretendardVariable.subset.11.woff2 +0 -0
  32. package/fonts/pretendard/PretendardVariable.subset.12.woff2 +0 -0
  33. package/fonts/pretendard/PretendardVariable.subset.13.woff2 +0 -0
  34. package/fonts/pretendard/PretendardVariable.subset.14.woff2 +0 -0
  35. package/fonts/pretendard/PretendardVariable.subset.15.woff2 +0 -0
  36. package/fonts/pretendard/PretendardVariable.subset.16.woff2 +0 -0
  37. package/fonts/pretendard/PretendardVariable.subset.17.woff2 +0 -0
  38. package/fonts/pretendard/PretendardVariable.subset.18.woff2 +0 -0
  39. package/fonts/pretendard/PretendardVariable.subset.19.woff2 +0 -0
  40. package/fonts/pretendard/PretendardVariable.subset.2.woff2 +0 -0
  41. package/fonts/pretendard/PretendardVariable.subset.20.woff2 +0 -0
  42. package/fonts/pretendard/PretendardVariable.subset.21.woff2 +0 -0
  43. package/fonts/pretendard/PretendardVariable.subset.22.woff2 +0 -0
  44. package/fonts/pretendard/PretendardVariable.subset.23.woff2 +0 -0
  45. package/fonts/pretendard/PretendardVariable.subset.24.woff2 +0 -0
  46. package/fonts/pretendard/PretendardVariable.subset.25.woff2 +0 -0
  47. package/fonts/pretendard/PretendardVariable.subset.26.woff2 +0 -0
  48. package/fonts/pretendard/PretendardVariable.subset.27.woff2 +0 -0
  49. package/fonts/pretendard/PretendardVariable.subset.28.woff2 +0 -0
  50. package/fonts/pretendard/PretendardVariable.subset.29.woff2 +0 -0
  51. package/fonts/pretendard/PretendardVariable.subset.3.woff2 +0 -0
  52. package/fonts/pretendard/PretendardVariable.subset.30.woff2 +0 -0
  53. package/fonts/pretendard/PretendardVariable.subset.31.woff2 +0 -0
  54. package/fonts/pretendard/PretendardVariable.subset.32.woff2 +0 -0
  55. package/fonts/pretendard/PretendardVariable.subset.33.woff2 +0 -0
  56. package/fonts/pretendard/PretendardVariable.subset.34.woff2 +0 -0
  57. package/fonts/pretendard/PretendardVariable.subset.35.woff2 +0 -0
  58. package/fonts/pretendard/PretendardVariable.subset.36.woff2 +0 -0
  59. package/fonts/pretendard/PretendardVariable.subset.37.woff2 +0 -0
  60. package/fonts/pretendard/PretendardVariable.subset.38.woff2 +0 -0
  61. package/fonts/pretendard/PretendardVariable.subset.39.woff2 +0 -0
  62. package/fonts/pretendard/PretendardVariable.subset.4.woff2 +0 -0
  63. package/fonts/pretendard/PretendardVariable.subset.40.woff2 +0 -0
  64. package/fonts/pretendard/PretendardVariable.subset.41.woff2 +0 -0
  65. package/fonts/pretendard/PretendardVariable.subset.42.woff2 +0 -0
  66. package/fonts/pretendard/PretendardVariable.subset.43.woff2 +0 -0
  67. package/fonts/pretendard/PretendardVariable.subset.44.woff2 +0 -0
  68. package/fonts/pretendard/PretendardVariable.subset.45.woff2 +0 -0
  69. package/fonts/pretendard/PretendardVariable.subset.46.woff2 +0 -0
  70. package/fonts/pretendard/PretendardVariable.subset.47.woff2 +0 -0
  71. package/fonts/pretendard/PretendardVariable.subset.48.woff2 +0 -0
  72. package/fonts/pretendard/PretendardVariable.subset.49.woff2 +0 -0
  73. package/fonts/pretendard/PretendardVariable.subset.5.woff2 +0 -0
  74. package/fonts/pretendard/PretendardVariable.subset.50.woff2 +0 -0
  75. package/fonts/pretendard/PretendardVariable.subset.51.woff2 +0 -0
  76. package/fonts/pretendard/PretendardVariable.subset.52.woff2 +0 -0
  77. package/fonts/pretendard/PretendardVariable.subset.53.woff2 +0 -0
  78. package/fonts/pretendard/PretendardVariable.subset.54.woff2 +0 -0
  79. package/fonts/pretendard/PretendardVariable.subset.55.woff2 +0 -0
  80. package/fonts/pretendard/PretendardVariable.subset.56.woff2 +0 -0
  81. package/fonts/pretendard/PretendardVariable.subset.57.woff2 +0 -0
  82. package/fonts/pretendard/PretendardVariable.subset.58.woff2 +0 -0
  83. package/fonts/pretendard/PretendardVariable.subset.59.woff2 +0 -0
  84. package/fonts/pretendard/PretendardVariable.subset.6.woff2 +0 -0
  85. package/fonts/pretendard/PretendardVariable.subset.60.woff2 +0 -0
  86. package/fonts/pretendard/PretendardVariable.subset.61.woff2 +0 -0
  87. package/fonts/pretendard/PretendardVariable.subset.62.woff2 +0 -0
  88. package/fonts/pretendard/PretendardVariable.subset.63.woff2 +0 -0
  89. package/fonts/pretendard/PretendardVariable.subset.64.woff2 +0 -0
  90. package/fonts/pretendard/PretendardVariable.subset.65.woff2 +0 -0
  91. package/fonts/pretendard/PretendardVariable.subset.66.woff2 +0 -0
  92. package/fonts/pretendard/PretendardVariable.subset.67.woff2 +0 -0
  93. package/fonts/pretendard/PretendardVariable.subset.68.woff2 +0 -0
  94. package/fonts/pretendard/PretendardVariable.subset.69.woff2 +0 -0
  95. package/fonts/pretendard/PretendardVariable.subset.7.woff2 +0 -0
  96. package/fonts/pretendard/PretendardVariable.subset.70.woff2 +0 -0
  97. package/fonts/pretendard/PretendardVariable.subset.71.woff2 +0 -0
  98. package/fonts/pretendard/PretendardVariable.subset.72.woff2 +0 -0
  99. package/fonts/pretendard/PretendardVariable.subset.73.woff2 +0 -0
  100. package/fonts/pretendard/PretendardVariable.subset.74.woff2 +0 -0
  101. package/fonts/pretendard/PretendardVariable.subset.75.woff2 +0 -0
  102. package/fonts/pretendard/PretendardVariable.subset.76.woff2 +0 -0
  103. package/fonts/pretendard/PretendardVariable.subset.77.woff2 +0 -0
  104. package/fonts/pretendard/PretendardVariable.subset.78.woff2 +0 -0
  105. package/fonts/pretendard/PretendardVariable.subset.79.woff2 +0 -0
  106. package/fonts/pretendard/PretendardVariable.subset.8.woff2 +0 -0
  107. package/fonts/pretendard/PretendardVariable.subset.80.woff2 +0 -0
  108. package/fonts/pretendard/PretendardVariable.subset.81.woff2 +0 -0
  109. package/fonts/pretendard/PretendardVariable.subset.82.woff2 +0 -0
  110. package/fonts/pretendard/PretendardVariable.subset.83.woff2 +0 -0
  111. package/fonts/pretendard/PretendardVariable.subset.84.woff2 +0 -0
  112. package/fonts/pretendard/PretendardVariable.subset.85.woff2 +0 -0
  113. package/fonts/pretendard/PretendardVariable.subset.86.woff2 +0 -0
  114. package/fonts/pretendard/PretendardVariable.subset.87.woff2 +0 -0
  115. package/fonts/pretendard/PretendardVariable.subset.88.woff2 +0 -0
  116. package/fonts/pretendard/PretendardVariable.subset.89.woff2 +0 -0
  117. package/fonts/pretendard/PretendardVariable.subset.9.woff2 +0 -0
  118. package/fonts/pretendard/PretendardVariable.subset.90.woff2 +0 -0
  119. package/fonts/pretendard/PretendardVariable.subset.91.woff2 +0 -0
  120. package/fonts.css +937 -0
  121. package/package.json +10 -5
  122. package/src/core/consent.d.mts +160 -0
  123. package/src/core/consent.mjs +567 -0
  124. package/src/core/fonts.d.mts +5 -0
  125. package/src/core/fonts.mjs +39 -0
  126. package/src/core/index.d.mts +2 -0
  127. package/src/core/index.mjs +5 -0
  128. package/src/tanstack-start.d.mts +14 -2
  129. package/src/tanstack-start.mjs +39 -1
  130. package/styles.css +45 -2
@@ -1,5 +1,14 @@
1
1
  import { brandIconLinks } from "./core/seo.mjs";
2
2
  import { gtmHeadScript } from "./core/gtm.mjs";
3
+ import { fontPreloadLinks } from "./core/fonts.mjs";
4
+ import { consentCookieGrantsAnalytics, consentHeadScript } from "./core/consent.mjs";
5
+
6
+ /**
7
+ * Send these on every route that uses `consent` (TanStack Start: the route's
8
+ * `headers` option). The head differs per visitor's cookie, so the page must
9
+ * never sit in a shared cache.
10
+ */
11
+ export { CONSENT_RESPONSE_HEADERS } from "./core/consent.mjs";
3
12
 
4
13
  /**
5
14
  * A route `head().scripts` entry that loads Google Tag Manager (D-031).
@@ -16,11 +25,35 @@ export function gtmHeadEntry(containerId) {
16
25
  return { children: gtmHeadScript(containerId) };
17
26
  }
18
27
 
28
+ /**
29
+ * The consent-gated head entry (D-034): one inline script. Without a current
30
+ * analytics grant in `cookie` it only pushes Consent Mode defaults (all
31
+ * denied) into the page's dataLayer — nothing contacts Google. With one, it
32
+ * also grants `analytics_storage` and runs Google's Tag Manager loader.
33
+ *
34
+ * `cookie` is the Cookie header on the server and `document.cookie` in the
35
+ * browser (TanStack Start: createIsomorphicFn), so both sides of hydration
36
+ * render the same script. Only the consent cookie is read from it; nothing
37
+ * from it is written into the page. Like gtmHeadEntry, no nonce: the router
38
+ * stamps `ssr.nonce`.
39
+ */
40
+ export function consentHeadEntry(options) {
41
+ const granted = consentCookieGrantsAnalytics(options?.cookie, {
42
+ policyVersion: options?.policyVersion,
43
+ ...(options.cookieName === undefined ? {} : { cookieName: options.cookieName }),
44
+ });
45
+ return { children: consentHeadScript({ granted, gtm: options.gtm }) };
46
+ }
47
+
19
48
  // `icons` is opt-in: the adapter cannot know where (or whether) a product
20
49
  // serves the linq-brand files, and a head that links icons the server 404s
21
50
  // is worse than one that links none. `true` takes the /brand default.
22
51
  // `gtm` is opt-in the same way, and per route: the product decides which
23
52
  // pages carry Tag Manager (public marketing and legal pages, not consoles).
53
+ // It loads Tag Manager unconditionally and stays for compatibility; products
54
+ // switch to `consent` (D-034), which loads it only after an opt-in.
55
+ // `fontPreload` is opt-in too: only the product knows the hashed URL its
56
+ // bundler gave the face (D-033). Appended after the icons.
24
57
  export function toTanStackHead(metadata, options = {}) {
25
58
  const icons = options.icons === true ? brandIconLinks() : options.icons ? brandIconLinks(options.icons) : [];
26
59
  const head = {
@@ -43,9 +76,14 @@ export function toTanStackHead(metadata, options = {}) {
43
76
  { rel: "canonical", href: metadata.canonical },
44
77
  ...metadata.alternates.map(({ hreflang, href }) => ({ rel: "alternate", hreflang, href })),
45
78
  ...icons,
79
+ ...(options.fontPreload === undefined ? [] : fontPreloadLinks(options.fontPreload)),
46
80
  ],
47
81
  };
48
- if (options.gtm !== undefined) head.scripts = [gtmHeadEntry(options.gtm)];
82
+ if (options.gtm !== undefined && options.consent !== undefined) {
83
+ throw new TypeError("toTanStackHead: pass the container id as consent.gtm, not both gtm and consent — gtm alone loads Tag Manager without consent");
84
+ }
85
+ if (options.consent !== undefined) head.scripts = [consentHeadEntry(options.consent)];
86
+ else if (options.gtm !== undefined) head.scripts = [gtmHeadEntry(options.gtm)];
49
87
  return head;
50
88
  }
51
89
 
package/styles.css CHANGED
@@ -73,7 +73,7 @@
73
73
  /* Touch: every link in the chrome is a 44px target (spec §6), as every button already is
74
74
  (dds-css button.css). Placed before the narrow block so its menu rows can override the display. */
75
75
  @media (pointer: coarse) {
76
- .site-brand, .site-nav__list a, .site-footer__links a, .site-footer__brand a, .site-footer__langs-trigger { display: inline-flex; align-items: center; min-block-size: 44px; }
76
+ .site-brand, .site-nav__list a, .site-footer__links a, .site-footer__links .site-consent-trigger, .site-footer__brand a, .site-footer__langs-trigger { display: inline-flex; align-items: center; min-block-size: 44px; }
77
77
  .site-footer__langs-list a { display: flex; align-items: center; min-block-size: 44px; }
78
78
  }
79
79
 
@@ -92,7 +92,7 @@
92
92
  }
93
93
 
94
94
  @media (prefers-reduced-motion: reduce) { .site-header { scroll-behavior: auto; } }
95
- @media (forced-colors: active) { .site-header, .site-footer, .site-status { forced-color-adjust: auto; } }
95
+ @media (forced-colors: active) { .site-header, .site-footer, .site-status, .site-consent { forced-color-adjust: auto; } }
96
96
 
97
97
  .site-locale-flag { position: relative; }
98
98
  .site-locale-flag__trigger { list-style: none; display: inline-grid; place-items: center; min-inline-size: 44px; min-block-size: 44px; border-radius: var(--dds-radius-sm); cursor: pointer; }
@@ -109,3 +109,46 @@
109
109
  .site-locale-flag__option:focus-visible { outline: 2px solid var(--dds-color-border-focus); outline-offset: -2px; }
110
110
  .site-locale-flag__option[aria-current="true"] { font-weight: 700; }
111
111
  @media (max-width: 720px) { .site-locale-flag__list { position: static; inline-size: 100%; max-block-size: none; box-shadow: none; } }
112
+
113
+ /* Consent (D-034): a bottom bar with three equal choices, and a settings dialog.
114
+ The three buttons share one tone and one grid track width, so no choice is
115
+ drawn heavier than another. */
116
+ /* Room under the page while the bar is up (ConsentBanner sets the height); zero specificity, so a product body rule wins. */
117
+ :where(body) { padding-block-end: var(--site-consent-block-size, 0px); }
118
+ .site-consent { position: fixed; inset-inline: 0; inset-block-end: 0; z-index: 900; max-block-size: 100dvh; overflow-y: auto; border-block-start: 1px solid var(--dds-color-border-default); background: var(--dds-color-bg-elevated); color: var(--dds-color-text-primary); box-shadow: var(--dds-elevation-3); }
119
+ .site-consent__inner { inline-size: min(100% - var(--dds-space-32), 1120px); margin-inline: auto; padding-block: var(--dds-space-16); display: grid; grid-template-columns: minmax(0, 1fr) auto auto; grid-template-areas: "copy actions close"; align-items: center; gap: var(--dds-space-12) var(--dds-space-24); }
120
+ .site-consent__copy { grid-area: copy; display: grid; gap: var(--dds-space-4); min-inline-size: 0; }
121
+ .site-consent__title { margin: 0; font-size: var(--dds-typo-body-1-font-size); line-height: var(--dds-typo-body-1-line-height); font-weight: 700; }
122
+ .site-consent__body { margin: 0; max-inline-size: 72ch; font-size: var(--dds-typo-body-2-font-size); line-height: var(--dds-typo-body-2-line-height); color: var(--dds-color-text-secondary); }
123
+ .site-consent__link { color: var(--dds-color-text-brand); text-decoration: underline; text-underline-offset: 2px; }
124
+ .site-consent__link:focus-visible, .site-consent-trigger:focus-visible { outline: 2px solid var(--dds-color-border-focus); outline-offset: 2px; border-radius: var(--dds-radius-sm); }
125
+ .site-consent__actions { grid-area: actions; display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: var(--dds-space-8); }
126
+ .site-consent__action { inline-size: 100%; min-inline-size: 7rem; }
127
+ .site-consent__dismiss { grid-area: close; align-self: start; }
128
+ .dds-dialog.site-consent-dialog { position: relative; max-inline-size: 36rem; max-block-size: calc(100dvh - 2 * var(--dds-space-16)); overflow-y: auto; }
129
+ .site-consent-dialog .dds-dialog__title { padding-inline-end: var(--dds-space-40); }
130
+ .site-consent-dialog__close { position: absolute; inset-block-start: var(--dds-space-12); inset-inline-end: var(--dds-space-12); }
131
+ .site-consent-categories { display: grid; margin: 0 0 var(--dds-space-12); padding: 0; list-style: none; }
132
+ .site-consent-category { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: start; gap: var(--dds-space-16); padding-block: var(--dds-space-12); border-block-start: 1px solid var(--dds-color-border-default); }
133
+ .site-consent-category:last-child { border-block-end: 1px solid var(--dds-color-border-default); }
134
+ .site-consent-category__copy { display: grid; gap: var(--dds-space-4); min-inline-size: 0; }
135
+ .site-consent-category__title { margin: 0; font-size: var(--dds-typo-label-font-size); line-height: var(--dds-typo-label-line-height); font-weight: 600; }
136
+ .site-consent-category__body { margin: 0; font-size: var(--dds-typo-body-2-font-size); line-height: var(--dds-typo-body-2-line-height); color: var(--dds-color-text-secondary); }
137
+ .site-consent-category__status, .site-consent-category__switch { justify-self: end; }
138
+ .site-consent-category__status { background: var(--dds-color-bg-subtle); color: var(--dds-color-text-secondary); }
139
+ .site-consent-dialog__policy { margin: 0 0 var(--dds-space-16); font-size: var(--dds-typo-body-2-font-size); }
140
+ .site-consent-dialog .dds-dialog__actions { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
141
+ /* The footer's "쿠키 설정" reads as one of its links. */
142
+ .site-consent-trigger { appearance: none; margin: 0; padding: 0; border: 0; background: none; font: inherit; color: var(--dds-color-text-secondary); cursor: pointer; }
143
+ .site-consent-trigger:hover { color: var(--dds-color-text-primary); text-decoration: underline; }
144
+ @media (max-width: 720px) {
145
+ .site-consent__inner { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "copy close" "actions actions"; align-items: start; }
146
+ .site-consent__actions { grid-auto-flow: row; grid-template-columns: repeat(3, minmax(0, 1fr)); }
147
+ .site-consent__action { min-inline-size: 0; }
148
+ }
149
+ @media (max-width: 480px) {
150
+ .site-consent-dialog .dds-dialog__actions { grid-template-columns: minmax(0, 1fr); }
151
+ }
152
+ @media (max-width: 340px) {
153
+ .site-consent__actions { grid-template-columns: minmax(0, 1fr); }
154
+ }