@publier/shell 0.2.0 → 0.3.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/dist/index.d.mts CHANGED
@@ -11,7 +11,7 @@
11
11
  * '@publier/shell/plugins/remark-structure' → search-index structure collector
12
12
  * '@publier/shell/plugins/remark-asides' → :::note / :::tip / :::caution / :::danger
13
13
  */
14
- import { g as docsShell, i as DocsShellOptions } from "./integration-Dvp9zu_X.mjs";
14
+ import { i as DocsShellOptions, v as docsShell } from "./integration-DGNkCfVa.mjs";
15
15
  import "astro/zod";
16
16
  /**
17
17
  * Cache-control + security headers emitted by `docsShell()` during
@@ -167,6 +167,19 @@ interface NavItem {
167
167
  href: string;
168
168
  external?: boolean;
169
169
  description?: string;
170
+ /** Mega menu: local path or https URL of a small decorative image. */
171
+ icon?: string;
172
+ }
173
+ /** Mega menu: a titled sub-group of a group; sub-groups nest up to three levels. */
174
+ interface NavGroup {
175
+ label: string;
176
+ items?: NavItem[];
177
+ groups?: NavGroup[];
178
+ }
179
+ /** Mega menu: the column shared by every group's panel (featured links). */
180
+ interface NavAside {
181
+ label?: string;
182
+ items: NavItem[];
170
183
  }
171
184
  /** A plain link, or a labelled group of one-level navigation items. */
172
185
  interface NavLink {
@@ -174,6 +187,14 @@ interface NavLink {
174
187
  href?: string;
175
188
  external?: boolean;
176
189
  items?: NavItem[];
190
+ /** Mega menu: titled sub-groups (a tree). */
191
+ groups?: NavGroup[];
192
+ /** Mega menu: sub-group columns on desktop, 1 to 4 (default 2). */
193
+ columns?: 1 | 2 | 3 | 4;
194
+ /** Mega menu: links under the group's body. */
195
+ footer?: NavItem[];
196
+ /** Mega menu: show a filter field in the group's panel. */
197
+ search?: boolean;
177
198
  }
178
199
  interface NavCta {
179
200
  label: string;
@@ -206,6 +227,8 @@ interface NavVariant {
206
227
  tabs: NavLink[];
207
228
  links: NavLink[];
208
229
  cta: NavCta | null;
230
+ /** Mega menu: featured links shown in every group's panel. */
231
+ aside?: NavAside;
209
232
  }
210
233
  interface NavPathRule {
211
234
  path: string;
@@ -220,4 +243,4 @@ interface NavConfig {
220
243
  searchHotkey: string;
221
244
  }
222
245
  declare function docsShell(options?: DocsShellOptions): AstroIntegration;
223
- export { hoistMediaOutOfUtilitiesLayer as _, DocsSidebarMetaOverride as a, NavCta as c, NavPathRule as d, NavProduct as f, docsShell as g, SiteConfig as h, DocsShellOptions as i, NavItem as l, SidebarAnchorConfig as m, DocVersionConfig as n, LlmsConfig as o, NavVariant as p, DocsConfig as r, NavConfig as s, AnnouncementConfig as t, NavLink as u, publierCascadeHoistPlugin as v };
246
+ export { SiteConfig as _, DocsSidebarMetaOverride as a, publierCascadeHoistPlugin as b, NavConfig as c, NavItem as d, NavLink as f, SidebarAnchorConfig as g, NavVariant as h, DocsShellOptions as i, NavCta as l, NavProduct as m, DocVersionConfig as n, LlmsConfig as o, NavPathRule as p, DocsConfig as r, NavAside as s, AnnouncementConfig as t, NavGroup as u, docsShell as v, hoistMediaOutOfUtilitiesLayer as y };
@@ -6,5 +6,5 @@
6
6
  * `injectScript`, `injectRoute`). The planner returns JSON "plans" the body
7
7
  * below iterates over and dispatches.
8
8
  */
9
- import { _ as hoistMediaOutOfUtilitiesLayer, a as DocsSidebarMetaOverride, c as NavCta, d as NavPathRule, f as NavProduct, g as docsShell, h as SiteConfig, i as DocsShellOptions, l as NavItem, m as SidebarAnchorConfig, n as DocVersionConfig, o as LlmsConfig, p as NavVariant, r as DocsConfig, s as NavConfig, t as AnnouncementConfig, u as NavLink, v as publierCascadeHoistPlugin } from "./integration-Dvp9zu_X.mjs";
10
- export { AnnouncementConfig, DocVersionConfig, DocsConfig, DocsShellOptions, DocsSidebarMetaOverride, LlmsConfig, NavConfig, NavCta, NavItem, NavLink, NavPathRule, NavProduct, NavVariant, SidebarAnchorConfig, SiteConfig, docsShell, hoistMediaOutOfUtilitiesLayer, publierCascadeHoistPlugin };
9
+ import { _ as SiteConfig, a as DocsSidebarMetaOverride, b as publierCascadeHoistPlugin, c as NavConfig, d as NavItem, f as NavLink, g as SidebarAnchorConfig, h as NavVariant, i as DocsShellOptions, l as NavCta, m as NavProduct, n as DocVersionConfig, o as LlmsConfig, p as NavPathRule, r as DocsConfig, s as NavAside, t as AnnouncementConfig, u as NavGroup, v as docsShell, y as hoistMediaOutOfUtilitiesLayer } from "./integration-DGNkCfVa.mjs";
10
+ export { AnnouncementConfig, DocVersionConfig, DocsConfig, DocsShellOptions, DocsSidebarMetaOverride, LlmsConfig, NavAside, NavConfig, NavCta, NavGroup, NavItem, NavLink, NavPathRule, NavProduct, NavVariant, SidebarAnchorConfig, SiteConfig, docsShell, hoistMediaOutOfUtilitiesLayer, publierCascadeHoistPlugin };
@@ -0,0 +1 @@
1
+ const e=[`almond`,`aspen`,`catppuccin`,`dark`,`dusk`,`emerald`,`enterprise`,`hearth`,`light`,`maple`,`neutral`,`ocean`,`purple`,`ruby`,`solar`,`vitepress`];function t(){return e}function n(t){return e.includes(t)}const r=[`display-xl`,`display-lg`,`display-md`,`heading-lg`,`heading-md`,`heading-sm`,`title`,`body-lg`,`body-md`,`body-sm`,`caption`,`label`,`button`,`code`],i=[`size`,`weight`,`line-height`,`tracking`];export{t as a,n as i,i as n,r,e as t};
@@ -5,7 +5,7 @@
5
5
  * Node-side introspection (reading CSS files, computing contrast) lives in
6
6
  * `./loader.ts` to keep `node:fs` out of the browser bundle path.
7
7
  */
8
- declare const AVAILABLE_THEMES: readonly ['almond', 'aspen', 'catppuccin', 'dark', 'dusk', 'emerald', 'enterprise', 'light', 'maple', 'neutral', 'ocean', 'purple', 'ruby', 'solar', 'vitepress'];
8
+ declare const AVAILABLE_THEMES: readonly ['almond', 'aspen', 'catppuccin', 'dark', 'dusk', 'emerald', 'enterprise', 'hearth', 'light', 'maple', 'neutral', 'ocean', 'purple', 'ruby', 'solar', 'vitepress'];
9
9
  type ThemePreset = (typeof AVAILABLE_THEMES)[number];
10
10
  /** Return a read-only copy of the built-in preset list. */
11
11
  declare function listPresets(): readonly ThemePreset[];
@@ -1 +1 @@
1
- import"@github/details-menu-element";const e=matchMedia(`(min-width: 1024px)`),t=matchMedia(`(hover: hover) and (pointer: fine)`),n=matchMedia(`(prefers-reduced-motion: reduce)`);let r,i,a;function o(){return i||=import(`motion/mini`).then(e=>{r=e.animate}).catch(()=>{i=void 0}),i}function s(){a?.();let i=Array.from(document.querySelectorAll(`[data-publier-top-nav]:not([data-publier-docs-header]) details.dropdown[data-publier-nav-group]:not([data-publier-docs-selector])`));if(!i.length)return;let s=new AbortController,c={signal:s.signal},l=/* @__PURE__ */ new Set;e.matches&&!n.matches&&o();for(let a of i){let i=a.querySelector(`:scope > summary`),u=a.querySelector(`:scope > details-menu`);if(!i||!u)continue;let d,f,p=0,m=!1,h=()=>{clearTimeout(d),d=void 0},g=()=>{p+=1,f?.cancel(),f=void 0},_=()=>{h(),g(),m=!1,a.open=!1};l.add(_),a.addEventListener(`pointerenter`,n=>{n.pointerType!==`touch`&&e.matches&&t.matches&&(h(),a.open||=(a.dispatchEvent(new MouseEvent(`mousedown`)),m=!0,!0))},c),a.addEventListener(`pointerleave`,e=>{e.pointerType!==`touch`&&(h(),d=setTimeout(()=>{u.contains(document.activeElement)||_()},160))},c),i.addEventListener(`click`,e=>{e.detail>0&&m&&a.open&&e.preventDefault(),m=!1},c),document.addEventListener(`keydown`,e=>{e.key===`Escape`&&!e.defaultPrevented&&a.open&&!a.contains(document.activeElement)&&(_(),e.preventDefault())},c),a.addEventListener(`focusout`,()=>{queueMicrotask(()=>{!a.contains(document.activeElement)&&!a.matches(`:hover`)&&_()})},c),a.addEventListener(`toggle`,()=>{if(g(),!a.open){m=!1;return}if(n.matches||!e.matches)return;let t=p;o().then(()=>{t!==p||s.signal.aborted||a.isConnected&&a.open&&!n.matches&&e.matches&&(f=r?.(u,{opacity:[0,1],transform:[`translateY(6px)`,`translateY(0)`]},{duration:.18,ease:[.16,1,.3,1]}))})},c),n.addEventListener(`change`,g,c)}e.addEventListener(`change`,()=>{for(let e of l)e()},c),t.addEventListener(`change`,()=>{for(let e of l)e()},c),a=()=>{s.abort();for(let e of l)e();a=void 0}}s(),document.addEventListener(`astro:before-swap`,()=>a?.()),document.addEventListener(`astro:after-swap`,s);export{};
1
+ import"@github/details-menu-element";function e(e){return e.normalize(`NFKC`).toLowerCase().replace(/\s+/gu,``)}function t(n,r,i){let a=i||r!==``&&e(n.title).includes(r),o=n.items.map(t=>r===``||a||e(t.title).includes(r)||e(t.description).includes(r)),s=n.sections.map(e=>t(e,r,a));return{visible:o.some(Boolean)||s.some(e=>e.visible),items:o,sections:s}}function n(n,r,i){let a=e(i),o=a!==``&&e(n).includes(a),s=r.map(e=>t(e,a,o));return{sections:s,empty:a!==``&&!s.some(e=>e.visible)}}const r=`[data-publier-nav-trigger]`,i=`[data-publier-nav-search]`,a=matchMedia(`(min-width: 1024px)`),o=matchMedia(`(hover: hover) and (pointer: fine)`),s=matchMedia(`(prefers-reduced-motion: reduce)`);let c,l,u;function d(){return l||=import(`motion/mini`).then(e=>{c=e.animate}).catch(()=>{l=void 0}),l}const f=e=>!e.closest(`dialog`),p=e=>e.querySelector(`:scope > summary`),m=e=>e.querySelector(`:scope > [data-publier-nav-panel]`);function h(e){return e?.textContent?.trim()??``}function g(e){return{title:h(e.querySelector(`:scope > [data-publier-nav-title]`)),items:Array.from(e.querySelectorAll(`:scope > ul > li[data-publier-nav-item]`)).map(e=>({title:h(e.querySelector(`[data-publier-nav-item-title]`)),description:h(e.querySelector(`[data-publier-nav-description]`))})),sections:Array.from(e.querySelectorAll(`:scope > [data-publier-nav-section]`)).map(g)}}function _(e){return Array.from(e.querySelectorAll(`[data-publier-nav-section]`)).filter(e=>e.parentElement?.closest(`[data-publier-nav-section]`)==null)}function v(e,t){e.hidden=!t.visible,e.querySelectorAll(`:scope > ul > li[data-publier-nav-item]`).forEach((e,n)=>{e.hidden=!(t.items[n]??!0)}),e.querySelectorAll(`:scope > [data-publier-nav-section]`).forEach((e,n)=>{let r=t.sections[n];r&&v(e,r)})}function y(e){e.hidden=!1;for(let t of e.querySelectorAll(`li[data-publier-nav-item]`))t.hidden=!1;for(let t of e.querySelectorAll(`[data-publier-nav-section]`))t.hidden=!1}const b=/* @__PURE__ */ new WeakMap;function x(e){let t=e.closest(`[data-publier-nav-panel]`),r=t?.querySelector(`[data-publier-nav-main]`);if(!t||!r)return;let i=_(r),a=b.get(r);a||(a=i.map(g),b.set(r,a));let o=n(t.closest(`[data-publier-nav-group]`)?.getAttribute(`data-publier-nav-group`)??``,a,e.value);if(e.value.trim()===``)for(let e of i)y(e);else i.forEach((e,t)=>{let n=o.sections[t];n&&v(e,n)});let s=r.querySelector(`[data-publier-nav-empty]`);s&&(s.hidden=!o.empty)}function S(e){let t=e.querySelector(i);t&&t.value!==``&&(t.value=``,x(t))}function C(e){let t=p(e),n=m(e);if(!t||!n)return;let r=t.getBoundingClientRect(),i=n.getBoundingClientRect();n.style.setProperty(`--publier-nav-mega-notch-x`,`${Math.round(r.left+r.width/2-i.left)}px`)}function w(){u?.();let e=Array.from(document.querySelectorAll(`details[data-publier-nav-mega-group]`));if(!e.length)return;let t=new AbortController,n={signal:t.signal},l=e.filter(f),h=/* @__PURE__ */ new Set,g=!1;a.matches&&!s.matches&&l.length&&d();let _=()=>l.filter(e=>e.open),v=e=>{g=_().some(t=>t!==e);for(let t of l)t!==e&&(t.open=!1);e.open=!0};for(let r of e){let e=p(r),i=m(r);if(!e||!i)continue;let l=f(r),u,y,b=0,x=!1,w=()=>{clearTimeout(u),u=void 0},T=()=>{b+=1,y?.cancel(),y=void 0},E=()=>{w(),T(),x=!1,r.open=!1};h.add(E),l&&(r.addEventListener(`pointerenter`,e=>{e.pointerType!==`touch`&&a.matches&&o.matches&&(w(),r.open||(x=!0,v(r)))},n),r.addEventListener(`pointerleave`,e=>{e.pointerType!==`touch`&&(w(),x&&(u=setTimeout(()=>{i.contains(document.activeElement)||E()},150)))},n),r.addEventListener(`focusout`,e=>{let t=e.relatedTarget;t instanceof Node&&r.contains(t)||setTimeout(()=>{r.open&&!r.contains(document.activeElement)&&(r.matches(`:hover`)||E())},0)},n)),e.addEventListener(`click`,e=>{e.detail>0&&x&&r.open?e.preventDefault():l&&!r.open&&(g=_().some(e=>e!==r)),x=!1},{...n,capture:!0}),r.addEventListener(`toggle`,()=>{if(T(),!r.open){x=!1,S(r);return}let e=g;if(g=!1,!l||(C(r),e||s.matches||!a.matches))return;let n=b;d().then(()=>{n!==b||t.signal.aborted||r.isConnected&&r.open&&!s.matches&&(y=c?.(i,{opacity:[0,1]},{duration:.15,ease:`easeOut`}))})},n),s.addEventListener(`change`,T,n)}let y=new Set(l.map(e=>e.closest(`[data-publier-top-nav]`)).filter(e=>e!==null));for(let e of y)e.addEventListener(`keydown`,t=>{let n=t.target instanceof Element?t.target.closest(r):null;if(!n||t.altKey||t.ctrlKey||t.metaKey)return;let a=Array.from(e.querySelectorAll(r)).filter(e=>e.offsetParent!==null),o=a.indexOf(n),s=n.parentElement instanceof HTMLDetailsElement?n.parentElement:null;if(t.key===`ArrowRight`||t.key===`ArrowLeft`){let e=a[(o+(t.key===`ArrowRight`?1:-1)+a.length)%a.length];if(!e)return;let n=_().length>0;e.focus();let r=e.parentElement instanceof HTMLDetailsElement?e.parentElement:null;if(n&&r&&l.includes(r))v(r);else if(n)for(let e of _())e.open=!1;t.preventDefault()}else t.key===`ArrowDown`&&s&&l.includes(s)&&(v(s),s.querySelector(`${i}, a[href]`)?.focus(),t.preventDefault())},n);document.addEventListener(`keydown`,e=>{if(e.key!==`Escape`||e.defaultPrevented)return;let t=(e.target instanceof Element?e.target:null)?.closest(i);if(t&&t.value!==``){t.value=``,x(t),e.preventDefault();return}let n=_()[0];if(!n)return;let r=n.contains(document.activeElement);for(let e of _())e.open=!1;r&&p(n)?.focus(),e.preventDefault()},n),document.addEventListener(`input`,e=>{let t=e.target instanceof Element?e.target.closest(i):null;t instanceof HTMLInputElement&&x(t)},n),document.addEventListener(`pointerdown`,e=>{if(e.target instanceof Element&&!e.target.closest(r))for(let t of _())t.contains(e.target)||(t.open=!1)},n),document.addEventListener(`click`,e=>{let t=e.target instanceof Element?e.target.closest(`[data-publier-nav-empty] [data-publier-search-button]`):null;if(t){for(let e of _())e.open=!1;t.closest(`dialog[data-publier-nav-dialog]`)?.close()}},n);let b=()=>{for(let e of h)e()};a.addEventListener(`change`,b,n),o.addEventListener(`change`,b,n),window.addEventListener(`resize`,()=>{for(let e of _())C(e)},n),u=()=>{t.abort(),b(),u=void 0}}function T(){w()}function E(){u?.()}const D=matchMedia(`(min-width: 1024px)`),O=matchMedia(`(hover: hover) and (pointer: fine)`),k=matchMedia(`(prefers-reduced-motion: reduce)`);let A,j,M;function N(){return j||=import(`motion/mini`).then(e=>{A=e.animate}).catch(()=>{j=void 0}),j}function P(){M?.();let e=Array.from(document.querySelectorAll(`[data-publier-top-nav]:not([data-publier-docs-header]) details.dropdown[data-publier-nav-group]:not([data-publier-docs-selector])`));if(!e.length)return;let t=new AbortController,n={signal:t.signal},r=/* @__PURE__ */ new Set;D.matches&&!k.matches&&N();for(let i of e){let e=i.querySelector(`:scope > summary`),a=i.querySelector(`:scope > details-menu`);if(!e||!a)continue;let o,s,c=0,l=!1,u=()=>{clearTimeout(o),o=void 0},d=()=>{c+=1,s?.cancel(),s=void 0},f=()=>{u(),d(),l=!1,i.open=!1};r.add(f),i.addEventListener(`pointerenter`,e=>{e.pointerType!==`touch`&&D.matches&&O.matches&&(u(),i.open||=(i.dispatchEvent(new MouseEvent(`mousedown`)),l=!0,!0))},n),i.addEventListener(`pointerleave`,e=>{e.pointerType!==`touch`&&(u(),o=setTimeout(()=>{a.contains(document.activeElement)||f()},160))},n),e.addEventListener(`click`,e=>{e.detail>0&&l&&i.open&&e.preventDefault(),l=!1},n),document.addEventListener(`keydown`,e=>{e.key===`Escape`&&!e.defaultPrevented&&i.open&&!i.contains(document.activeElement)&&(f(),e.preventDefault())},n),i.addEventListener(`focusout`,()=>{queueMicrotask(()=>{!i.contains(document.activeElement)&&!i.matches(`:hover`)&&f()})},n),i.addEventListener(`toggle`,()=>{if(d(),!i.open){l=!1;return}if(k.matches||!D.matches)return;let e=c;N().then(()=>{e!==c||t.signal.aborted||i.isConnected&&i.open&&!k.matches&&D.matches&&(s=A?.(a,{opacity:[0,1],transform:[`translateY(6px)`,`translateY(0)`]},{duration:.18,ease:[.16,1,.3,1]}))})},n),k.addEventListener(`change`,d,n)}D.addEventListener(`change`,()=>{for(let e of r)e()},n),O.addEventListener(`change`,()=>{for(let e of r)e()},n),M=()=>{t.abort();for(let e of r)e();M=void 0}}P(),T(),document.addEventListener(`astro:before-swap`,()=>{M?.(),E()}),document.addEventListener(`astro:after-swap`,()=>{P(),T()});export{};
@@ -1,3 +1,3 @@
1
- import { i as listPresets, n as ThemePreset, r as isValidPreset, t as AVAILABLE_THEMES } from "../presets-Bk89M8E_.mjs";
1
+ import { i as listPresets, n as ThemePreset, r as isValidPreset, t as AVAILABLE_THEMES } from "../presets-PH6ZjfE7.mjs";
2
2
  export declare const PRESET_CSS_PATH: '@publier/shell/tailwind/preset.css';
3
3
  export { AVAILABLE_THEMES, type ThemePreset, isValidPreset, listPresets };
@@ -1 +1 @@
1
- import{n as e,r as t,t as n}from"../presets-CYFX0GLA.mjs";const r=`@publier/shell/tailwind/preset.css`;export{n as AVAILABLE_THEMES,r as PRESET_CSS_PATH,e as isValidPreset,t as listPresets};
1
+ import{a as e,i as t,t as n}from"../presets-D_MQnAtK.mjs";const r=`@publier/shell/tailwind/preset.css`;export{n as AVAILABLE_THEMES,r as PRESET_CSS_PATH,t as isValidPreset,e as listPresets};
@@ -4,7 +4,7 @@
4
4
  * Reads built-in preset files from disk. Node-only (uses `node:fs` via native).
5
5
  * Theme resolution / override composition lives in `./override.ts`.
6
6
  */
7
- import { i as listPresets, n as ThemePreset, r as isValidPreset, t as AVAILABLE_THEMES } from "../presets-Bk89M8E_.mjs";
7
+ import { i as listPresets, n as ThemePreset, r as isValidPreset, t as AVAILABLE_THEMES } from "../presets-PH6ZjfE7.mjs";
8
8
  /** Shape of a parsed `theme.yaml`. Fields match `content-spec/theme.schema.json`. */
9
9
  interface ThemeYaml {
10
10
  /**
@@ -25,6 +25,11 @@ interface ThemeYaml {
25
25
  layout?: Record<string, string>;
26
26
  prose?: Record<string, string>;
27
27
  code?: Record<string, string>;
28
+ spacing?: Record<string, string>;
29
+ radius?: Record<string, string>;
30
+ elevation?: Record<string, string>;
31
+ motion?: Record<string, string>;
32
+ variants?: Record<string, string>;
28
33
  logo?: {
29
34
  light?: string;
30
35
  dark?: string;
@@ -32,7 +37,7 @@ interface ThemeYaml {
32
37
  favicon?: string;
33
38
  }
34
39
  /** Allow-listed override sections — parallel to the JSON Schema. */
35
- export declare const THEME_SECTIONS: readonly ['colors', 'typography', 'layout', 'prose', 'code'];
40
+ export declare const THEME_SECTIONS: readonly ['colors', 'typography', 'layout', 'prose', 'code', 'spacing', 'radius', 'elevation', 'motion', 'variants'];
36
41
  type ThemeSection = (typeof THEME_SECTIONS)[number];
37
42
  /**
38
43
  * Keys accepted under each section. Mirrors content-spec/theme.schema.json.
@@ -1,2 +1,2 @@
1
- import{n as e,r as t,t as n}from"../presets-CYFX0GLA.mjs";import{tailwindComposeThemeCss as r,tailwindReadPresetCss as i,tailwindResolveTheme as a}from"@publier/native";import{fileURLToPath as o}from"node:url";const s=[`colors`,`typography`,`layout`,`prose`,`code`],c={colors:[`primary`,`primary-content`,`secondary`,`secondary-content`,`accent`,`accent-content`,`neutral`,`neutral-content`,`base-100`,`base-200`,`base-300`,`base-content`,`info`,`info-content`,`success`,`success-content`,`warning`,`warning-content`,`error`,`error-content`],typography:[`heading`,`body`,`mono`,`body-weight`,`heading-weight`],layout:[`nav-height`,`sidebar-width`,`toc-width`,`page-max-width`],prose:[`content-gap-y`,`line-height`,`heading-line-height`],code:[`block-radius`,`inline-radius`,`inline-padding-y`,`inline-padding-x`]};function l(e){if(!e||typeof e!=`object`||Array.isArray(e))return{preset:void 0,overrides:{},overrideCss:``,errors:[]};let t=a(JSON.stringify(e));return{preset:t.preset??void 0,overrides:t.overrides,overrideCss:t.overrideCss,errors:t.errors.map(e=>({code:e.code,message:e.message,path:e.path}))}}function u(e){let t=Object.keys(e).sort();return t.length===0?``:`:root[data-theme="light"], :root[data-theme="dark"] {\n${t.map(t=>`\t${t}: ${e[t]};`).join(`
2
- `)}\n}\n`}function d(e,t){let n=l(e),r=t.defaultPreset;return n.preset!==void 0&&(t.isValidPreset(n.preset)?r=n.preset:n.errors.push({code:`unknown-preset`,message:`Unknown preset "${n.preset}". Theme will fall back to "${t.defaultPreset}".`,path:`preset`})),{...n,preset:r}}function f(e,t){return r(e,t)}const p=o(new URL(`./themes/`,import.meta.url));function m(e){return`${p}${e}.css`}async function h(e){return i(p,e)}export{n as AVAILABLE_THEMES,c as THEME_KEYS,s as THEME_SECTIONS,f as composeThemeCss,e as isValidPreset,t as listPresets,h as loadPresetCss,m as presetPath,u as renderOverrideCss,l as resolveTheme,d as resolveThemeWithPreset};
1
+ import{a as e,i as t,n,r,t as i}from"../presets-D_MQnAtK.mjs";import{tailwindComposeThemeCss as a,tailwindReadPresetCss as o,tailwindResolveTheme as s}from"@publier/native";import{fileURLToPath as c}from"node:url";const l=[`colors`,`typography`,`layout`,`prose`,`code`,`spacing`,`radius`,`elevation`,`motion`,`variants`],u={colors:[`primary`,`primary-content`,`secondary`,`secondary-content`,`accent`,`accent-content`,`neutral`,`neutral-content`,`base-100`,`base-200`,`base-300`,`base-content`,`info`,`info-content`,`success`,`success-content`,`warning`,`warning-content`,`error`,`error-content`],typography:[`heading`,`body`,`mono`,`body-weight`,`heading-weight`,...r.flatMap(e=>n.map(t=>`scale-${e}-${t}`))],layout:[`nav-height`,`sidebar-width`,`toc-width`,`page-max-width`],prose:[`content-gap-y`,`line-height`,`heading-line-height`],code:[`block-radius`,`inline-radius`,`inline-padding-y`,`inline-padding-x`],spacing:[`xxs`,`xs`,`sm`,`md`,`base`,`lg`,`xl`,`xxl`,`section`],radius:[`button`,`control`,`card`,`panel`,`chip`],elevation:[`flat`,`raised`,`overlay`],motion:[`duration-fast`,`duration-base`,`duration-slow`,`easing`],variants:[`nav`,`button`,`card`,`hero`,`section`]};function d(e){if(!e||typeof e!=`object`||Array.isArray(e))return{preset:void 0,overrides:{},overrideCss:``,errors:[]};let t=s(JSON.stringify(e));return{preset:t.preset??void 0,overrides:t.overrides,overrideCss:t.overrideCss,errors:t.errors.map(e=>({code:e.code,message:e.message,path:e.path}))}}function f(e){let t=Object.keys(e).sort();return t.length===0?``:`:root[data-theme="light"], :root[data-theme="dark"] {\n${t.map(t=>`\t${t}: ${e[t]};`).join(`
2
+ `)}\n}\n`}function p(e,t){let n=d(e),r=t.defaultPreset;return n.preset!==void 0&&(t.isValidPreset(n.preset)?r=n.preset:n.errors.push({code:`unknown-preset`,message:`Unknown preset "${n.preset}". Theme will fall back to "${t.defaultPreset}".`,path:`preset`})),{...n,preset:r}}function m(e,t){return a(e,t)}const h=c(new URL(`./themes/`,import.meta.url));function g(e){return`${h}${e}.css`}async function _(e){return o(h,e)}export{i as AVAILABLE_THEMES,u as THEME_KEYS,l as THEME_SECTIONS,m as composeThemeCss,t as isValidPreset,e as listPresets,_ as loadPresetCss,g as presetPath,f as renderOverrideCss,d as resolveTheme,p as resolveThemeWithPreset};
@@ -0,0 +1,227 @@
1
+ :root, [data-theme="light"] {
2
+ color-scheme: light;
3
+
4
+ --color-base-100: #ffffff;
5
+ --color-base-200: #f7f7f7;
6
+ --color-base-300: #ebebeb;
7
+ --color-base-content: #222222;
8
+ --color-primary: #c2410c;
9
+ --color-primary-content: #ffffff;
10
+ --color-secondary: #3f3f3f;
11
+ --color-secondary-content: #ffffff;
12
+ --color-accent: #c2410c;
13
+ --color-accent-content: #ffffff;
14
+ --color-neutral: #222222;
15
+ --color-neutral-content: #ffffff;
16
+ --color-error: #c13515;
17
+ --color-error-content: #ffffff;
18
+
19
+ --radius-selector: 9999px;
20
+ --radius-field: 8px;
21
+ --radius-box: 14px;
22
+ --border: 1px;
23
+ --depth: 0;
24
+ --noise: 0;
25
+ }
26
+
27
+ [data-theme="dark"] {
28
+ color-scheme: dark;
29
+
30
+ --color-base-100: #1a1817;
31
+ --color-base-200: #24211f;
32
+ --color-base-300: #3a3634;
33
+ --color-base-content: #ececec;
34
+ --color-primary: #fb923c;
35
+ --color-primary-content: #1a1817;
36
+ --color-secondary: #a8a29e;
37
+ --color-secondary-content: #1a1817;
38
+ --color-accent: #fb923c;
39
+ --color-accent-content: #1a1817;
40
+ --color-neutral: #ececec;
41
+ --color-neutral-content: #1a1817;
42
+ --color-error: #f2765a;
43
+ --color-error-content: #1a1817;
44
+
45
+ --radius-selector: 9999px;
46
+ --radius-field: 8px;
47
+ --radius-box: 14px;
48
+ --border: 1px;
49
+ --depth: 0;
50
+ --noise: 0;
51
+ }
52
+
53
+ @media (prefers-color-scheme: dark) {
54
+ :root:not([data-theme]) {
55
+ color-scheme: dark;
56
+
57
+ --color-base-100: #1a1817;
58
+ --color-base-200: #24211f;
59
+ --color-base-300: #3a3634;
60
+ --color-base-content: #ececec;
61
+ --color-primary: #fb923c;
62
+ --color-primary-content: #1a1817;
63
+ --color-secondary: #a8a29e;
64
+ --color-secondary-content: #1a1817;
65
+ --color-accent: #fb923c;
66
+ --color-accent-content: #1a1817;
67
+ --color-neutral: #ececec;
68
+ --color-neutral-content: #1a1817;
69
+ --color-error: #f2765a;
70
+ --color-error-content: #1a1817;
71
+
72
+ --radius-selector: 9999px;
73
+ --radius-field: 8px;
74
+ --radius-box: 14px;
75
+ --border: 1px;
76
+ --depth: 0;
77
+ --noise: 0;
78
+ }
79
+ }
80
+
81
+ :root {
82
+
83
+ --publier-scale-display-xl-size: 3.5rem;
84
+ --publier-scale-display-xl-weight: 600;
85
+ --publier-scale-display-xl-line-height: 1.1;
86
+ --publier-scale-display-xl-tracking: -0.02em;
87
+ --publier-scale-display-lg-size: 2.75rem;
88
+ --publier-scale-display-lg-weight: 600;
89
+ --publier-scale-display-lg-line-height: 1.12;
90
+ --publier-scale-display-lg-tracking: -0.02em;
91
+ --publier-scale-display-md-size: 2rem;
92
+ --publier-scale-display-md-weight: 600;
93
+ --publier-scale-display-md-line-height: 1.2;
94
+ --publier-scale-display-md-tracking: -0.01em;
95
+ --publier-scale-heading-lg-size: 1.75rem;
96
+ --publier-scale-heading-lg-weight: 600;
97
+ --publier-scale-heading-lg-line-height: 1.3;
98
+ --publier-scale-heading-lg-tracking: 0;
99
+ --publier-scale-heading-md-size: 1.375rem;
100
+ --publier-scale-heading-md-weight: 500;
101
+ --publier-scale-heading-md-line-height: 1.18;
102
+ --publier-scale-heading-md-tracking: -0.02em;
103
+ --publier-scale-heading-sm-size: 1.25rem;
104
+ --publier-scale-heading-sm-weight: 600;
105
+ --publier-scale-heading-sm-line-height: 1.2;
106
+ --publier-scale-heading-sm-tracking: 0;
107
+ --publier-scale-title-size: 1rem;
108
+ --publier-scale-title-weight: 600;
109
+ --publier-scale-title-line-height: 1.25;
110
+ --publier-scale-title-tracking: 0;
111
+ --publier-scale-body-lg-size: 1.125rem;
112
+ --publier-scale-body-lg-weight: 400;
113
+ --publier-scale-body-lg-line-height: 1.55;
114
+ --publier-scale-body-lg-tracking: 0;
115
+ --publier-scale-body-md-size: 1rem;
116
+ --publier-scale-body-md-weight: 400;
117
+ --publier-scale-body-md-line-height: 1.5;
118
+ --publier-scale-body-md-tracking: 0;
119
+ --publier-scale-body-sm-size: 0.875rem;
120
+ --publier-scale-body-sm-weight: 400;
121
+ --publier-scale-body-sm-line-height: 1.43;
122
+ --publier-scale-body-sm-tracking: 0;
123
+ --publier-scale-caption-size: 0.8125rem;
124
+ --publier-scale-caption-weight: 400;
125
+ --publier-scale-caption-line-height: 1.23;
126
+ --publier-scale-caption-tracking: 0;
127
+ --publier-scale-button-size: 1rem;
128
+ --publier-scale-button-weight: 500;
129
+ --publier-scale-button-line-height: 1.25;
130
+ --publier-scale-button-tracking: 0;
131
+ --publier-scale-label-size: 0.875rem;
132
+ --publier-scale-label-weight: 500;
133
+ --publier-scale-label-line-height: 1.29;
134
+ --publier-scale-label-tracking: 0;
135
+ --publier-scale-code-size: 0.875rem;
136
+ --publier-scale-code-weight: 400;
137
+ --publier-scale-code-line-height: 1.6;
138
+ --publier-scale-code-tracking: 0;
139
+
140
+ --publier-font-weight-body: 400;
141
+ --publier-font-weight-heading: 600;
142
+
143
+ --publier-space-xxs: 0.125rem;
144
+ --publier-space-xs: 0.25rem;
145
+ --publier-space-sm: 0.5rem;
146
+ --publier-space-md: 0.75rem;
147
+ --publier-space-base: 1rem;
148
+ --publier-space-lg: 1.5rem;
149
+ --publier-space-xl: 2rem;
150
+ --publier-space-xxl: 3rem;
151
+ --publier-space-section: 4rem;
152
+
153
+ --publier-radius-button: 8px;
154
+ --publier-radius-control: 8px;
155
+ --publier-radius-card: 14px;
156
+ --publier-radius-panel: 20px;
157
+ --publier-radius-chip: 9999px;
158
+ --publier-control-radius: 8px;
159
+ --publier-surface-radius: 14px;
160
+ --publier-card-radius: 14px;
161
+ --publier-nav-item-radius: 8px;
162
+ --publier-code-block-radius: 8px;
163
+ --publier-code-inline-radius: 6px;
164
+
165
+ --publier-elevation-flat: none;
166
+ --publier-elevation-raised:
167
+ 0 0 0 1px rgb(0 0 0 / 0.02), 0 2px 6px rgb(0 0 0 / 0.04), 0 4px 8px rgb(0 0 0 / 0.1);
168
+ --publier-elevation-overlay:
169
+ 0 0 0 1px rgb(0 0 0 / 0.02), 0 2px 6px rgb(0 0 0 / 0.04), 0 4px 8px rgb(0 0 0 / 0.1);
170
+
171
+ --publier-motion-duration-fast: 120ms;
172
+ --publier-motion-duration-base: 150ms;
173
+ --publier-motion-duration-slow: 240ms;
174
+ --publier-motion-easing: cubic-bezier(0.2, 0, 0, 1);
175
+
176
+ --publier-nav-height: 4.5rem;
177
+ --publier-page-max-width: 45rem;
178
+ --publier-layout-width: 1280px;
179
+ --publier-shadow-color: 0 0 0;
180
+ }
181
+
182
+ :root,
183
+ :root[data-theme="light"] {
184
+ --color-pl-muted: #6a6a6a;
185
+ --color-pl-muted-bg: #f2f2f2;
186
+ --color-pl-subtle: #6a6a6a;
187
+ --color-pl-card: #ffffff;
188
+ --color-pl-card-hover: #ffffff;
189
+ --color-pl-popover: #ffffff;
190
+ --color-pl-border: #dddddd;
191
+ --color-pl-border-strong: #c1c1c1;
192
+ --color-pl-ring: #222222;
193
+ --color-pl-sidebar-accent: #f7f7f7;
194
+ --color-pl-sidebar-active: #fff4ed;
195
+ --color-pl-sidebar-active-fg: #c2410c;
196
+
197
+ --menu-active-bg: var(--color-pl-sidebar-active);
198
+ --menu-active-color: var(--color-pl-sidebar-active-fg);
199
+ --menu-hover-bg: var(--color-pl-sidebar-accent);
200
+ --menu-hover-color: var(--color-pl-sidebar-accent-fg);
201
+ }
202
+
203
+ :root[data-theme="dark"],
204
+ .dark {
205
+ --color-pl-muted: #a8a29e;
206
+ --color-pl-muted-bg: #24211f;
207
+ --color-pl-subtle: #a8a29e;
208
+ --color-pl-card: #24211f;
209
+ --color-pl-card-hover: #24211f;
210
+ --color-pl-popover: #24211f;
211
+ --color-pl-border: #3a3634;
212
+ --color-pl-border-strong: #4a4644;
213
+ --color-pl-ring: #ececec;
214
+ --color-pl-sidebar-accent: #24211f;
215
+ --color-pl-sidebar-active: color-mix(in oklch, #fb923c 14%, transparent);
216
+ --color-pl-sidebar-active-fg: #fb923c;
217
+
218
+ --publier-elevation-raised:
219
+ 0 0 0 1px #3a3634, 0 2px 6px rgb(0 0 0 / 0.08), 0 4px 8px rgb(0 0 0 / 0.2);
220
+ --publier-elevation-overlay:
221
+ 0 0 0 1px #3a3634, 0 2px 6px rgb(0 0 0 / 0.08), 0 4px 8px rgb(0 0 0 / 0.2);
222
+
223
+ --menu-active-bg: var(--color-pl-sidebar-active);
224
+ --menu-active-color: var(--color-pl-sidebar-active-fg);
225
+ --menu-hover-bg: var(--color-pl-sidebar-accent);
226
+ --menu-hover-color: var(--color-pl-sidebar-accent-fg);
227
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@publier/shell",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "license": "UNLICENSED",
5
5
  "description": "Qwik + Astro docs layout for Publier sites.",
6
6
  "type": "module",
@@ -119,7 +119,7 @@
119
119
  "@github/details-menu-element": "^1.0.13",
120
120
  "@github/relative-time-element": "^5.3.1",
121
121
  "@github/tab-container-element": "^4.9.0",
122
- "@publier/native": "0.1.1",
122
+ "@publier/native": "0.2.0",
123
123
  "@tailwindcss/typography": "^0.5.20",
124
124
  "astro-pagefind": "^2.0.1",
125
125
  "daisyui": "^5.7.50",
@@ -4,8 +4,8 @@ import { navConfig, type NavVariant } from 'virtual:publier-nav-config';
4
4
  import type { TopNavMobileProps } from './types.ts';
5
5
 
6
6
  const EMPTY: NavVariant = { brand: '', logo: null, tabs: [], links: [], cta: null };
7
- const { variant = 'default', currentPath = '', variantConfig } = Astro.props as TopNavMobileProps;
7
+ const { variant = 'default', currentPath = '', variantConfig, navVariant } = Astro.props as TopNavMobileProps;
8
8
  const v = variantConfig ?? navConfig.variants[variant] ?? navConfig.variants.default ?? EMPTY;
9
- const html = shellRenderTopNavMobile(JSON.stringify({ currentPath, v }));
9
+ const html = shellRenderTopNavMobile(JSON.stringify({ currentPath, v, navVariant, searchEnabled: navConfig.searchEnabled }));
10
10
  ---
11
11
  <Fragment set:html={html} />
@@ -4,7 +4,7 @@ import { navConfig, type NavVariant } from 'virtual:publier-nav-config';
4
4
  import type { TopNavProps } from './types.ts';
5
5
 
6
6
  const EMPTY: NavVariant = { brand: '', logo: null, tabs: [], links: [], cta: null };
7
- const { variant = 'default', currentPath = '', variantConfig } = Astro.props as TopNavProps;
7
+ const { variant = 'default', currentPath = '', variantConfig, navVariant } = Astro.props as TopNavProps;
8
8
  const v = variantConfig ?? navConfig.variants[variant] ?? navConfig.variants.default ?? EMPTY;
9
9
  const [searchSlotHtml, themeSlotHtml, mobileSlotHtml] = await Promise.all([
10
10
  Astro.slots.has('search') ? Astro.slots.render('search') : Promise.resolve(null),
@@ -12,7 +12,7 @@ const [searchSlotHtml, themeSlotHtml, mobileSlotHtml] = await Promise.all([
12
12
  Astro.slots.has('mobile') ? Astro.slots.render('mobile') : Promise.resolve(null),
13
13
  ]);
14
14
  const html = shellRenderTopNav(JSON.stringify({
15
- variant, currentPath, v,
15
+ variant, navVariant, currentPath, v,
16
16
  searchEnabled: navConfig.searchEnabled,
17
17
  searchSlotHtml, themeSlotHtml, mobileSlotHtml,
18
18
  }));
@@ -479,12 +479,16 @@ export interface TopNavProps {
479
479
  variantConfig?: import('virtual:publier-nav-config').NavVariant;
480
480
  /** Current page path for active highlighting. Pass `Astro.url.pathname`. */
481
481
  currentPath?: string;
482
+ /** Navigation style: `"mega"` renders group panels; anything else is the simple dropdown nav. */
483
+ navVariant?: string;
482
484
  }
483
485
 
484
486
  export interface TopNavMobileProps {
485
487
  variant?: string;
486
488
  variantConfig?: import('virtual:publier-nav-config').NavVariant;
487
489
  currentPath?: string;
490
+ /** Same value as `TopNavProps.navVariant`. */
491
+ navVariant?: string;
488
492
  }
489
493
 
490
494
  // ---------------------------------------------------------------------------
@@ -12,6 +12,7 @@ import { feeds } from 'virtual:publier-feeds';
12
12
  import { fonts } from 'virtual:publier-fonts';
13
13
  import { navConfig, type NavVariant } from 'virtual:publier-nav-config';
14
14
  import { site } from 'virtual:publier-site';
15
+ import { fullPreset, variants } from 'virtual:publier-theme-variants';
15
16
 
16
17
  interface Props { title: string; description?: string; nav?: string | false; navConfig?: NavVariant; class?: string; lang?: string }
17
18
  const { title, description, nav: navProp, navConfig: resolvedNav, class: className, lang = Astro.currentLocale ?? 'en' } = Astro.props;
@@ -28,6 +29,10 @@ if (navProp !== undefined) {
28
29
  : (JSON.parse(matched) as string);
29
30
  }
30
31
 
32
+ const htmlHooks = fullPreset
33
+ ? Object.fromEntries([['data-full-preset', ''], ...Object.entries(variants).map(([k, v]) => [`data-variant-${k}`, v])])
34
+ : {};
35
+
31
36
  const emailProtectionStart = '<!--email_off-->';
32
37
  const emailProtectionEnd = '<!--/email_off-->';
33
38
  const storageKey = announcement?.storageKey ?? 'publier-announcement';
@@ -38,7 +43,7 @@ const [head, bodyOpen, bodyClose] = JSON.parse(shellRenderBaseLayout(JSON.string
38
43
  }))) as [string, string, string];
39
44
  ---
40
45
  <!doctype html>
41
- <html lang={lang}>
46
+ <html lang={lang} {...htmlHooks}>
42
47
  <head>
43
48
  <Fragment set:html={head} />
44
49
  <Font cssVariable="--publier-font-builtin-sans" preload />
@@ -59,10 +64,10 @@ const [head, bodyOpen, bodyClose] = JSON.parse(shellRenderBaseLayout(JSON.string
59
64
  <slot name="skip" />
60
65
  <Fragment set:html={bodyOpen} />
61
66
  {navVariant !== false && (
62
- <TopNav variant={navVariant} variantConfig={resolvedNav} currentPath={currentPath}>
67
+ <TopNav variant={navVariant} variantConfig={resolvedNav} currentPath={currentPath} navVariant={variants.nav}>
63
68
  {navConfig.searchEnabled && <SearchButton slot="search" />}
64
69
  <ThemeToggle slot="theme" default={appearance.default} strict={appearance.strict} />
65
- <TopNavMobile slot="mobile" variant={navVariant} variantConfig={resolvedNav} currentPath={currentPath} />
70
+ <TopNavMobile slot="mobile" variant={navVariant} variantConfig={resolvedNav} currentPath={currentPath} navVariant={variants.nav} />
66
71
  </TopNav>
67
72
  )}
68
73
  <slot />
@@ -193,3 +193,122 @@
193
193
  --color-pl-sidebar-active-fg: var(--color-primary);
194
194
  --color-pl-sidebar-border: var(--color-pl-border);
195
195
  }
196
+
197
+ /* ─── Full-preset base layer ────────────────────────────────
198
+ * Every element a full preset can set, with neutral defaults, so a preset or
199
+ * `theme.yaml` can override any one of them and the colour-only presets keep
200
+ * looking as they do today. Cascade: this base, then the preset's theme file,
201
+ * then `theme.yaml` overrides, then `customCss`.
202
+ *
203
+ * The consuming rules below apply only when the document carries
204
+ * `data-full-preset` (a full preset, or a `theme.yaml` that sets one of these
205
+ * sections). Renderers emit the same markup either way; presets switch
206
+ * components through `data-variant-*` hooks on <html>. */
207
+ @layer publier-theme {
208
+ :root {
209
+ --publier-radius-button: var(--radius-field, 0.5rem);
210
+ --publier-radius-control: var(--radius-field, 0.5rem);
211
+ --publier-radius-card: var(--radius-box, 0.75rem);
212
+ --publier-radius-panel: var(--radius-box, 0.75rem);
213
+ --publier-radius-chip: var(--radius-selector, 0.5rem);
214
+
215
+ --publier-space-xxs: 0.125rem;
216
+ --publier-space-xs: 0.25rem;
217
+ --publier-space-sm: 0.5rem;
218
+ --publier-space-md: 0.75rem;
219
+ --publier-space-base: 1rem;
220
+ --publier-space-lg: 1.5rem;
221
+ --publier-space-xl: 2rem;
222
+ --publier-space-xxl: 3rem;
223
+ --publier-space-section: 4rem;
224
+
225
+ --publier-elevation-flat: none;
226
+ --publier-elevation-raised: var(--shadow-sm);
227
+ --publier-elevation-overlay: var(--shadow-lg);
228
+
229
+ --publier-motion-duration-fast: 100ms;
230
+ --publier-motion-duration-base: 150ms;
231
+ --publier-motion-duration-slow: 240ms;
232
+ --publier-motion-easing: cubic-bezier(0.4, 0, 0.2, 1);
233
+ }
234
+
235
+ html[data-full-preset] {
236
+ --publier-transition-duration: var(--publier-motion-duration-base);
237
+ --publier-transition-timing: var(--publier-motion-easing);
238
+ --publier-card-radius: var(--publier-radius-card);
239
+ --publier-control-radius: var(--publier-radius-control);
240
+ --publier-surface-radius: var(--publier-radius-panel);
241
+ }
242
+
243
+ html[data-full-preset] .prose :where(h1) {
244
+ font-size: var(--publier-scale-display-lg-size);
245
+ font-weight: var(--publier-scale-display-lg-weight);
246
+ line-height: var(--publier-scale-display-lg-line-height);
247
+ letter-spacing: var(--publier-scale-display-lg-tracking);
248
+ }
249
+ html[data-full-preset] .prose :where(h2) {
250
+ font-size: var(--publier-scale-heading-lg-size);
251
+ font-weight: var(--publier-scale-heading-lg-weight);
252
+ line-height: var(--publier-scale-heading-lg-line-height);
253
+ letter-spacing: var(--publier-scale-heading-lg-tracking);
254
+ }
255
+ html[data-full-preset] .prose :where(h3) {
256
+ font-size: var(--publier-scale-heading-md-size);
257
+ font-weight: var(--publier-scale-heading-md-weight);
258
+ line-height: var(--publier-scale-heading-md-line-height);
259
+ letter-spacing: var(--publier-scale-heading-md-tracking);
260
+ }
261
+ html[data-full-preset] .prose :where(h4) {
262
+ font-size: var(--publier-scale-heading-sm-size);
263
+ font-weight: var(--publier-scale-heading-sm-weight);
264
+ line-height: var(--publier-scale-heading-sm-line-height);
265
+ }
266
+ html[data-full-preset] .prose :where(p, li) {
267
+ font-size: var(--publier-scale-body-md-size);
268
+ line-height: var(--publier-scale-body-md-line-height);
269
+ }
270
+ html[data-full-preset] .prose :where(.lead) {
271
+ font-size: var(--publier-scale-body-lg-size);
272
+ line-height: var(--publier-scale-body-lg-line-height);
273
+ }
274
+ html[data-full-preset] .btn {
275
+ font-size: var(--publier-scale-label-size);
276
+ font-weight: var(--publier-scale-label-weight);
277
+ border-radius: var(--publier-radius-button);
278
+ }
279
+ html[data-full-preset] .badge {
280
+ border-radius: var(--publier-radius-chip);
281
+ }
282
+ html[data-full-preset] .card {
283
+ border-radius: var(--publier-radius-card);
284
+ }
285
+ html[data-full-preset] :where(.dropdown-content, .menu.dropdown-content, [popover]) {
286
+ box-shadow: var(--publier-elevation-overlay);
287
+ border-radius: var(--publier-radius-card);
288
+ }
289
+ html[data-full-preset] :where(section, .publier-section) {
290
+ --publier-section-gap: var(--publier-space-section);
291
+ }
292
+
293
+ /* Component variants. */
294
+ html[data-full-preset][data-variant-button="pill"] .btn {
295
+ border-radius: 9999px;
296
+ }
297
+ html[data-full-preset][data-variant-button="square"] .btn {
298
+ border-radius: 0;
299
+ }
300
+ html[data-full-preset][data-variant-card="elevated"] .card {
301
+ box-shadow: var(--publier-elevation-raised);
302
+ border-color: transparent;
303
+ }
304
+ html[data-full-preset][data-variant-card="filled"] .card {
305
+ background-color: var(--color-pl-muted-bg);
306
+ border-color: transparent;
307
+ }
308
+ html[data-full-preset][data-variant-card="hairline"] .card {
309
+ border: 1px solid var(--color-pl-border);
310
+ }
311
+ html[data-full-preset][data-variant-card="hairline"] .card:hover {
312
+ box-shadow: var(--publier-elevation-raised);
313
+ }
314
+ }
@@ -21,6 +21,13 @@ declare module 'virtual:publier-appearance' {
21
21
  }
22
22
 
23
23
  declare module 'virtual:publier-theme-css';
24
+
25
+ declare module 'virtual:publier-theme-variants' {
26
+ import type { VariantSet } from './tailwind/presets.ts';
27
+ /** True for a full preset, or when `theme.yaml` sets a non-colour element. */
28
+ export const fullPreset: boolean;
29
+ export const variants: VariantSet;
30
+ }
24
31
  declare module 'virtual:publier-custom-css';
25
32
 
26
33
  declare module 'virtual:publier-nav-config' {
@@ -29,6 +36,21 @@ declare module 'virtual:publier-nav-config' {
29
36
  href: string;
30
37
  external?: boolean;
31
38
  description?: string;
39
+ /** Mega menu: local path or https URL of a small decorative image. */
40
+ icon?: string;
41
+ }
42
+
43
+ /** Mega menu: a titled sub-group of a group; sub-groups nest up to three levels. */
44
+ export interface NavGroup {
45
+ label: string;
46
+ items?: NavItem[];
47
+ groups?: NavGroup[];
48
+ }
49
+
50
+ /** Mega menu: the column shared by every group's panel (featured links). */
51
+ export interface NavAside {
52
+ label?: string;
53
+ items: NavItem[];
32
54
  }
33
55
 
34
56
  /** A plain link, or a labelled group of one-level navigation items. */
@@ -37,6 +59,14 @@ declare module 'virtual:publier-nav-config' {
37
59
  href?: string;
38
60
  external?: boolean;
39
61
  items?: NavItem[];
62
+ /** Mega menu: titled sub-groups (a tree). */
63
+ groups?: NavGroup[];
64
+ /** Mega menu: sub-group columns on desktop, 1 to 4 (default 2). */
65
+ columns?: 1 | 2 | 3 | 4;
66
+ /** Mega menu: links under the group's body. */
67
+ footer?: NavItem[];
68
+ /** Mega menu: show a filter field in the group's panel. */
69
+ search?: boolean;
40
70
  }
41
71
 
42
72
  export interface NavCta {
@@ -69,6 +99,8 @@ declare module 'virtual:publier-nav-config' {
69
99
  links: NavLink[];
70
100
  /** Primary CTA button. Null when absent. */
71
101
  cta: NavCta | null;
102
+ /** Mega menu: featured links shown in every group's panel. */
103
+ aside?: NavAside;
72
104
  }
73
105
 
74
106
  /**
@@ -1 +0,0 @@
1
- const e=[`almond`,`aspen`,`catppuccin`,`dark`,`dusk`,`emerald`,`enterprise`,`light`,`maple`,`neutral`,`ocean`,`purple`,`ruby`,`solar`,`vitepress`];function t(){return e}function n(t){return e.includes(t)}export{n,t as r,e as t};