diff --git a/frontend/components/landing/feature-showcase.ts b/frontend/components/landing/feature-showcase.ts index a5be8899..d933083f 100644 --- a/frontend/components/landing/feature-showcase.ts +++ b/frontend/components/landing/feature-showcase.ts @@ -38,8 +38,7 @@ const featureShowcaseTemplate = html`{{ linkLabel }} + >{{ linkLabel }} ` export function defineFeatureShowcaseComponent() { diff --git a/frontend/components/landing/regor-relationship.ts b/frontend/components/landing/regor-relationship.ts index adf83274..75a0f353 100644 --- a/frontend/components/landing/regor-relationship.ts +++ b/frontend/components/landing/regor-relationship.ts @@ -7,13 +7,24 @@ export interface RegorRelationship { const regorRelationshipTemplate = html`
- Reactive UI + Reactive UI
Regor @@ -22,9 +33,12 @@ const regorRelationshipTemplate = html`
PureStack
- Documentation + Documentation
-
{{ uiCaption }}
{{ docsCaption }}
+
+ {{ uiCaption }}
{{ docsCaption }} +
` export function defineRegorRelationshipComponent() { diff --git a/frontend/components/landing/source-workbench.ts b/frontend/components/landing/source-workbench.ts index fa89e4f5..f6cb2d5e 100644 --- a/frontend/components/landing/source-workbench.ts +++ b/frontend/components/landing/source-workbench.ts @@ -23,14 +23,11 @@ const sourceWorkbenchTemplate = html` + > + > + > Same source. Connected workflow. diff --git a/frontend/components/shared/copy-button.ts b/frontend/components/shared/copy-button.ts index a268e3cd..c2aa83a9 100644 --- a/frontend/components/shared/copy-button.ts +++ b/frontend/components/shared/copy-button.ts @@ -15,8 +15,7 @@ const copyButtonTemplate = html`Copy` + >Copy` export function defineCopyButtonComponent() { return defineComponent(copyButtonTemplate, { diff --git a/frontend/components/shell/palette-picker.ts b/frontend/components/shell/palette-picker.ts index 482aec11..3291cd5e 100644 --- a/frontend/components/shell/palette-picker.ts +++ b/frontend/components/shell/palette-picker.ts @@ -4,27 +4,34 @@ import { palettePresets } from '../../theme/presets' export interface PalettePicker {} const template = html`
- +
Site color

Choose your site’s color palette.

-
- ${palettePresets.map(renderColorOption).join('')} -
+
${palettePresets.map(renderColorOption).join('')}
Saved on this device.
-
` + +` export function definePalettePickerComponent() { return defineComponent(template) } function renderColorOption(preset: (typeof palettePresets)[number]) { - return html`` + return html`` } diff --git a/frontend/designs/social-preview.svg b/frontend/designs/social-preview.svg index bbac5421..3a49dff3 100644 --- a/frontend/designs/social-preview.svg +++ b/frontend/designs/social-preview.svg @@ -1,4 +1,5 @@ + PureStack: One language. A whole frontend. diff --git a/frontend/docs/apiProperty.ts b/frontend/docs/apiProperty.ts index f1f6ca44..1f1ecfe2 100644 --- a/frontend/docs/apiProperty.ts +++ b/frontend/docs/apiProperty.ts @@ -11,8 +11,7 @@ const apiPropertyTemplate = html`

{{ name }} + >{{ name }}

{{ valueType }}
diff --git a/frontend/docs/componentGuide.ts b/frontend/docs/componentGuide.ts index 09316054..3993b781 100644 --- a/frontend/docs/componentGuide.ts +++ b/frontend/docs/componentGuide.ts @@ -24,16 +24,20 @@ export function defineComponentGuideComponents() { return { ComponentHeader: defineComponent( html`
-
-
{{ name }}{{ kind }}
- -
-

{{ summary }}

- {{ section }} -
`, +
+
+ {{ name }}{{ kind }} +
+ +
+

{{ summary }}

+ {{ section }} +`, { props: ['name', 'summary', 'source', 'kind', 'section', 'sectionHref'], context: (head) => ({ @@ -45,10 +49,12 @@ export function defineComponentGuideComponents() { ), ComponentExample: defineComponent( html`
-
{{ label }}COMPOSITION
-
-

{{ caption }}

-
`, +
+ {{ label }}COMPOSITION +
+
+

{{ caption }}

+`, { props: ['label', 'caption'] }, ), } diff --git a/frontend/docs/dataGuide.ts b/frontend/docs/dataGuide.ts index 022a2bed..662ec4d8 100644 --- a/frontend/docs/dataGuide.ts +++ b/frontend/docs/dataGuide.ts @@ -76,11 +76,29 @@ export interface ChartAppearanceControls { export function defineChartAppearanceControls() { return defineComponent( html` - - - - - `, + + + + +`, { props: ['tone', 'variant', 'variantMode', 'width', 'showWidth', 'prefix'], context: (head) => ({ @@ -104,13 +122,43 @@ export interface ChartAppearanceGallery { export function defineChartAppearanceGallery() { return defineComponent( html``, +
+ {{ sample.label }} + + + +
+`, { props: ['component', 'axis'], context: (head) => { diff --git a/frontend/docs/formAppearance.ts b/frontend/docs/formAppearance.ts index 0099424a..f66102ee 100644 --- a/frontend/docs/formAppearance.ts +++ b/frontend/docs/formAppearance.ts @@ -44,26 +44,103 @@ const inputTypes = [ 'week', ] -const template = html`
+const template = html`
{{ sample.label }} - - + + - {{ sample.receipt }} + {{ sample.receipt }} - - - -
- - {{ sample.receipt }} + + + + + + {{ sample.receipt }} - Your draft is ready for review. - - - - + Your draft is ready for review. + + + +
` diff --git a/frontend/docs/landingGuide.ts b/frontend/docs/landingGuide.ts index 18dc4212..9122c127 100644 --- a/frontend/docs/landingGuide.ts +++ b/frontend/docs/landingGuide.ts @@ -32,23 +32,183 @@ export interface LandingAppearanceGallery { export function defineLandingAppearanceGallery() { return defineComponent( html``, +
+ {{ sample.label }} +
+ A new chapter +
+
+ Ready to compose +
+
+ +
+
+ +
+
+ +
+
+ +
<Badge>Ready</Badge>
+
+
+
+
    + Reusable patterns +
+
+
+
    + Reusable patterns +
+
+
+
    + One shared theme
    Across every screen.
    +
+
+
+ +
+
+ Theme-aware components +
+
+ Theme-aware components +
+
+
    + Theme-aware components +
+
+
+
`, { props: ['component', 'axis'], context: (head) => { diff --git a/frontend/docs/modal.ts b/frontend/docs/modal.ts index 0176fa9d..1eb5af9f 100644 --- a/frontend/docs/modal.ts +++ b/frontend/docs/modal.ts @@ -26,16 +26,14 @@ const modalApiTemplate = html` >

This dialog was opened by a Regor event handler.

+ >Close with the API
` const modalStoreTemplate = html` Review a saved draft + >Review a saved draft

{{ result }}

` diff --git a/frontend/docs/semanticToneGuide.ts b/frontend/docs/semanticToneGuide.ts index 3980b6ab..06db7bf0 100644 --- a/frontend/docs/semanticToneGuide.ts +++ b/frontend/docs/semanticToneGuide.ts @@ -56,21 +56,60 @@ export interface SemanticToneGallery { export function defineSemanticToneGallery() { return defineComponent( html``, +
+ {{ sample.label }} +
+ {{ sample.description }} +

One palette. Several useful roles.

+
+ Alternate surface +
+ Button role +
+
+ {{ sample.description }} +
+ Inset accent +
+
+ Shared light +
+ Glass follows the same origin +
+
+
+ +
+

{{ sample.note }}

+
+`, { props: ['axis'], context: (head) => { diff --git a/frontend/docs/sitePreviewComponents.ts b/frontend/docs/sitePreviewComponents.ts index 3820883d..b0c2902d 100644 --- a/frontend/docs/sitePreviewComponents.ts +++ b/frontend/docs/sitePreviewComponents.ts @@ -32,7 +32,12 @@ export interface TopBarPreview { frameStyle: string } -const sitePreviewTemplate = html`` +const sitePreviewTemplate = html`` function resolveSitePreview(site: SiteConfig, slug: string, height: number) { return { diff --git a/frontend/docs/sitePreviews.ts b/frontend/docs/sitePreviews.ts index d3bcfe1b..2a0f96db 100644 --- a/frontend/docs/sitePreviews.ts +++ b/frontend/docs/sitePreviews.ts @@ -181,7 +181,13 @@ export interface SiteAppearanceGallery { export function defineSiteAppearanceGallery(site: SiteConfig) { return defineComponent( - html``, + html``, { props: ['component', 'axis'], context: (head) => ({ diff --git a/frontend/purestack.studio/components/actions/alert-box/alert-box.mdx b/frontend/purestack.studio/components/actions/alert-box/alert-box.mdx index d40cd231..809fa569 100644 --- a/frontend/purestack.studio/components/actions/alert-box/alert-box.mdx +++ b/frontend/purestack.studio/components/actions/alert-box/alert-box.mdx @@ -13,12 +13,37 @@ nav: # AlertBox - + ## In context - -

Explain what happened, why it matters, and what the reader can do next.

+ +
+

+ Explain what happened, why it matters, and what the reader can do next. +

+
+
```html @@ -34,26 +59,37 @@ nav: Make the next step part of the message. The actions slot keeps it alongside the context. - + -
- -

Use semantic tones to communicate intent across your interface.

- -
-
-
+
+ +

Use semantic tones to communicate intent across your interface.

+ +
+
+ ```html + -
- - - The preview is ready for review. - - - Check the content before publishing. - - - Correct the source error and build again. - - -
-
+
+ + + The preview is ready for review. + + + Check the content before publishing. + + + Correct the source error and build again. + + +
+
```html @@ -116,57 +159,166 @@ Keep the message explicit so color is never the only signal. All ten tones, with visible labels. Explain intent through the message as well as color. - -
-
neutralA clear message with supporting context.
-
accentA clear message with supporting context.
-
featureA clear message with supporting context.
-
secondaryA clear message with supporting context.
-
customA clear message with supporting context.
-
ghostA clear message with supporting context.
-
infoA clear message with supporting context.
-
successA clear message with supporting context.
-
warningA clear message with supporting context.
-
dangerA clear message with supporting context.
-
+ +
+
+ neutralA clear message with supporting context. +
+
+ accentA clear message with supporting context. +
+
+ featureA clear message with supporting context. +
+
+ secondaryA clear message with supporting context. +
+
+ customA clear message with supporting context. +
+
+ ghostA clear message with supporting context. +
+
+ infoA clear message with supporting context. +
+
+ successA clear message with supporting context. +
+
+ warningA clear message with supporting context. +
+
+ dangerA clear message with supporting context. +
+
## All visual variants All eighteen treatments in the info tone. Content, icons, and actions share the same API across treatments. - -
-
solidSupporting message.
-
surfaceSupporting message.
-
surfaceAltSupporting message.
-
spotlightSupporting message.
-
glassSupporting message.
-
flatSupporting message.
-
flatAltSupporting message.
-
flatSolidSupporting message.
-
outlineFillSupporting message.
-
outlineSupporting message.
-
subtleSupporting message.
-
subtleBtnSupporting message.
-
linkSupporting message.
-
sheenSupporting message.
-
underlineSupporting message.
-
railSupporting message.
-
bracketSupporting message.
-
noneSupporting message.
-
+ +
+
+ solidSupporting message. +
+
+ surfaceSupporting message. +
+
+ surfaceAltSupporting message. +
+
+ spotlightSupporting message. +
+
+ glassSupporting message. +
+
+ flatSupporting message. +
+
+ flatAltSupporting message. +
+
+ flatSolidSupporting message. +
+
+ outlineFillSupporting message. +
+
+ outlineSupporting message. +
+
+ subtleSupporting message. +
+
+ subtleBtnSupporting message. +
+
+ linkSupporting message. +
+
+ sheenSupporting message. +
+
+ underlineSupporting message. +
+
+ railSupporting message. +
+
+ bracketSupporting message. +
+
+ noneSupporting message. +
+
## Variant modes Stateless is the default. Stateful enables hover, focus, and active styling; it does not add action handlers. Hover or focus these notices to compare. - -
-
statelessCompare the treatment.
-
statefulCompare the treatment.
-
+ +
+
+ statelessCompare the treatment. +
+
+ statefulCompare the treatment. +
+
## Behavior and accessibility @@ -177,7 +329,11 @@ AlertBox is a visual content component and does not automatically announce messa

AlertBox contract

-

Props below are the public template API. RefOrValue accepts a literal or a reactive ref; bind refs with a colon-prefixed attribute. Native attributes and events can be passed through the component root.

+

+ Props below are the public template API. RefOrValue accepts a literal or a + reactive ref; bind refs with a colon-prefixed attribute. Native attributes + and events can be passed through the component root. +

- -

Visible heading. Choose a short title that identifies the content or task.

+ +

+ Visible heading. Choose a short title that identifies the content or task. +

- +

Short context above the heading, such as a category or release label.

- +

Optional compact label rendered with Badge.

- +

Secondary context such as an update time, limitation or reassurance.

- -

Semantic message tone. The default is info; use success, warning or danger only when that intent matches the text.

+ +

+ Semantic message tone. The default is info; use success, warning or danger + only when that intent matches the text. +

- -

Visual treatment. Accepts solid, surface, surfaceAlt, spotlight, glass, flat, flatAlt, flatSolid, outlineFill, outline, subtle, subtleBtn, link, sheen, underline, rail, bracket or none.

+ +

+ Visual treatment. Accepts solid, surface, surfaceAlt, spotlight, glass, + flat, flatAlt, flatSolid, outlineFill, outline, subtle, subtleBtn, link, + sheen, underline, rail, bracket or none. +

- -

Use stateless for content surfaces; stateful enables the treatment’s hover, focus and active styles. It does not add interaction handlers.

+ +

+ Use stateless for content surfaces; stateful enables the treatment’s hover, + focus and active styles. It does not add interaction handlers. +

- -

Registered SVG icon name. In browser apps, include the icon in the resolver passed to defineIconComponents.

+ +

+ Registered SVG icon name. In browser apps, include the icon in the resolver + passed to defineIconComponents. +

### Composition
-

Default slot: message body. actions: contextual links or buttons. Register defineAlertComponents with Panel, Flex, IconFrame, Icon and Badge dependencies; action components need their own registrations.

+

+ Default slot: message body. + actions: contextual links or buttons. Register + defineAlertComponents with Panel, Flex, IconFrame, Icon and Badge + dependencies; action components need their own registrations. +

diff --git a/frontend/purestack.studio/components/actions/badge/badge-playground.ts b/frontend/purestack.studio/components/actions/badge/badge-playground.ts index 26864d1d..9046affd 100644 --- a/frontend/purestack.studio/components/actions/badge/badge-playground.ts +++ b/frontend/purestack.studio/components/actions/badge/badge-playground.ts @@ -28,8 +28,16 @@ const badgePlaygroundTemplate = html` - - + + Reset playground
` diff --git a/frontend/purestack.studio/components/actions/badge/badge.mdx b/frontend/purestack.studio/components/actions/badge/badge.mdx index 4c96c1d7..c921efbf 100644 --- a/frontend/purestack.studio/components/actions/badge/badge.mdx +++ b/frontend/purestack.studio/components/actions/badge/badge.mdx @@ -13,14 +13,28 @@ nav: # Badge - + Badge renders an inline `span` with two appearance props and a default slot. Its content describes something; use [Btn](/components/actions/buttons/) or [BtnLink](/components/actions/btn-link/) when the reader should take an action. ## In context - -

Design system

Stable

A small set of labels tells the release story at a glance.

v1.012 componentsTheme awareReview pending
+ +
+

Design system

+ Stable
+

A small set of labels tells the release story at a glance.

+ v1.012 componentsTheme awareReview pending +
```html @@ -50,17 +64,17 @@ Edit the label and explore every tone and variant. The default surface treatment tabVariant="underline" > -
- -
-
+
+ +
+
```html ``` - +
@@ -77,21 +91,21 @@ Use tone to reinforce the meaning already expressed by the label. “Passing”, tabVariant="underline" > -
- - neutral - accent - feature - secondary - custom - ghost - info - success - warning - danger - -
-
+
+ + neutral + accent + feature + secondary + custom + ghost + info + success + warning + danger + +
+ ```html @@ -123,29 +137,29 @@ Badge accepts every shared variant and defaults to `surface`. Its variants use s tabVariant="underline" > -
- - solid - surface - surfaceAlt - spotlight - glass - flat - flatAlt - flatSolid - outlineFill - outline - subtle - subtleBtn - link - sheen - underline - rail - bracket - none - -
-
+
+ + solid + surface + surfaceAlt + spotlight + glass + flat + flatAlt + flatSolid + outlineFill + outline + subtle + subtleBtn + link + sheen + underline + rail + bracket + none + +
+
```html @@ -185,22 +199,27 @@ The default slot accepts text and inline components. Put a badge beside a headin tabVariant="underline" > -
- - - Public API - Stable - v1.0 +
+ + + Public API + Stable + v1.0 + +

+ Open reviews 3 +

+ + + All checks passed +
-

Open reviews 3

- - - All checks passed - - -
- - +
+
+ ```html @@ -233,17 +252,17 @@ This example runs entirely in your browser. It models a release workflow without tabVariant="underline" > -
- -
-
+
+ +
+
```html ``` - +
@@ -259,8 +278,13 @@ This example runs entirely in your browser. It models a release workflow without ## API reference
- <Badge tone="success">Stable</Badge> -

A native <span> with two appearance props and one default slot. The default variant is surface. Static badges need no browser application.

+ <Badge tone="success">Stable</Badge> +

+ A native <span> with two appearance props and one default + slot. The default variant is surface. Static badges need no + browser application. +

Try submit and reset with reactive form state + >Try submit and reset with reactive form state

```html Save project @@ -563,9 +578,7 @@ Use native form behavior and boolean state to control what the action can do.
defaultButton label + >defaultButton label

Text or inline markup placed between the tags becomes the visible label. It is hidden when iconOnly is true. Avoid nested links, buttons, @@ -580,8 +593,7 @@ Use native form behavior and boolean state to control what the action can do.

@click(event: MouseEvent) => void + >@click(event: MouseEvent) => void

Fires on pointer or keyboard activation when the button is enabled. The handler must live in a mounted Regor app; static MDX does not run browser diff --git a/frontend/purestack.studio/components/actions/buttons/collection.ts b/frontend/purestack.studio/components/actions/buttons/collection.ts index d24b0735..ce3d1c90 100644 --- a/frontend/purestack.studio/components/actions/buttons/collection.ts +++ b/frontend/purestack.studio/components/actions/buttons/collection.ts @@ -32,8 +32,14 @@ const collectionTemplate = html` Add item - Remove item - Reset + Remove item + Reset ` diff --git a/frontend/purestack.studio/components/actions/index.mdx b/frontend/purestack.studio/components/actions/index.mdx index 5d4ff901..84406a5d 100644 --- a/frontend/purestack.studio/components/actions/index.mdx +++ b/frontend/purestack.studio/components/actions/index.mdx @@ -13,26 +13,127 @@ nav: # Actions and feedback -

Make the next step obvious. Make the outcome clear.

+

+ Make the next step obvious. Make the outcome clear. +

From a single button to a complete feedback loop, these seven components help people move through your interface with confidence. Explore practical examples, every supported tone and variant, and the complete public API. ## Choose your building block
-
Create projectPreview

Btn

Native action

A clear next step. Theme-aware actions with native button semantics, expressive variants, and just enough API.

Explore Btn
-
Explore guides

BtnLink

Navigation

Give a destination the emphasis it deserves. Button styling with the familiar behavior of a real link.

Explore BtnLink
-
DayWeekMonth

BtnGroup

Action layout

Related actions, one coherent row. Control alignment and wrapping while every child keeps its own identity.

Explore BtnGroup
-
All components

BtnGroupDropDown

Native disclosure

Keep the primary action in reach and its alternatives close by. Style the trigger and menu independently.

Explore BtnGroupDropDown
-
Stablev1.0In review

Badge

Inline metadata

A little context, right where it matters. Status, counts, and versions that make an interface easier to scan.

Explore Badge
-
Keep the next step close to the message.

AlertBox

Contextual feedback

Turn an important message into a useful next step. Combine context, semantic color, and actions in one notice.

Explore AlertBox
-
Draft saved. You’re all set.

ToastHost

Transient feedback

Acknowledge the moment without interrupting the flow. Dismissible notifications with predictable lifetimes.

Explore ToastHost
+
+
+ Create projectPreview +
+

Btn

+ Native action +

+ A clear next step. Theme-aware actions with native button semantics, + expressive variants, and just enough API. +

+ Explore Btn +
+
+
+ Explore guides +
+

BtnLink

+ Navigation +

+ Give a destination the emphasis it deserves. Button styling with the + familiar behavior of a real link. +

+ Explore BtnLink +
+
+
+ DayWeekMonth +
+

BtnGroup

+ Action layout +

+ Related actions, one coherent row. Control alignment and wrapping while + every child keeps its own identity. +

+ Explore BtnGroup +
+
+
+ All components +
+

BtnGroupDropDown

+ Native disclosure +

+ Keep the primary action in reach and its alternatives close by. Style the + trigger and menu independently. +

+ Explore BtnGroupDropDown +
+
+
+ Stablev1.0In review +
+

Badge

+ Inline metadata +

+ A little context, right where it matters. Status, counts, and versions + that make an interface easier to scan. +

+ Explore Badge +
+
+
+ Keep the next step close to the message. +
+

AlertBox

+ Contextual feedback +

+ Turn an important message into a useful next step. Combine context, + semantic color, and actions in one notice. +

+ Explore AlertBox +
+
+
+ Draft saved. You’re all set. +
+

ToastHost

+ Transient feedback +

+ Acknowledge the moment without interrupting the flow. Dismissible + notifications with predictable lifetimes. +

+ Explore ToastHost +
## Design the feedback loop - -

Release checklist

Review pending
Review your content before publishing.Open guidesExplore notifications
+ +
+

Release checklist

+ Review pending
Review your content before publishing.Open guidesExplore notifications +
## Pick the right feedback diff --git a/frontend/purestack.studio/components/actions/toast-host/playground.ts b/frontend/purestack.studio/components/actions/toast-host/playground.ts index ce733c28..270fe747 100644 --- a/frontend/purestack.studio/components/actions/toast-host/playground.ts +++ b/frontend/purestack.studio/components/actions/toast-host/playground.ts @@ -22,16 +22,26 @@ export interface ToastHostExample { } const toastHostExampleTemplate = html` - + Save local draft - Show review reminder - Show persistent error + Show review reminder + Show persistent error Clear notifications -

Try every supported tone. Info and success last 5 seconds, warning lasts 10; all others stay until dismissed.

+

+ Try every supported tone. Info and success last 5 seconds, warning lasts 10; + all others stay until dismissed. +

- {{ tone }} + {{ tone }} {{ notifications.items.length }} active notifications
` @@ -73,5 +83,5 @@ createApp( ...defineIconComponents((name) => icons[name] ?? ''), }, }, - { selector: 'app#toast-host-demo', template: html`` }, + { selector: 'app#toast-host-demo', template: html`` }, ) diff --git a/frontend/purestack.studio/components/actions/toast-host/toast-host.mdx b/frontend/purestack.studio/components/actions/toast-host/toast-host.mdx index e6be0cf5..3f9f2f32 100644 --- a/frontend/purestack.studio/components/actions/toast-host/toast-host.mdx +++ b/frontend/purestack.studio/components/actions/toast-host/toast-host.mdx @@ -13,12 +13,28 @@ nav: # ToastHost - + ## In context - -

AFTER AN ACTION

Local draft saved.5 seconds

Keep a toast brief. Put persistent instructions next to the task in an AlertBox.

+ +
+

AFTER AN ACTION

+ Local draft saved.5 seconds +

+ Keep a toast brief. Put persistent instructions next to the task in an + AlertBox. +

+
```html @@ -36,19 +52,26 @@ nav: Success disappears after five seconds; warning after ten. The error stays until dismissed. Notifications appear at the top of the viewport. - + -
- -
-
+
+ +
+ ```html ``` - +
@@ -56,19 +79,62 @@ Success disappears after five seconds; warning after ten. The error stays until ToastHost always uses the surface variant and has no variant prop. All ten supported ToastStore tones appear below as inline illustrations. - -
-
neutralneutral notificationUntil dismissed
-
accentaccent notificationUntil dismissed
-
featurefeature notificationUntil dismissed
-
secondarysecondary notificationUntil dismissed
-
customcustom notificationUntil dismissed
-
ghostghost notificationUntil dismissed
-
infoinfo notification5 seconds
-
successsuccess notification5 seconds
-
warningwarning notification10 seconds
-
dangerdanger notificationUntil dismissed
-
+ +
+
+ neutralneutral notificationUntil dismissed +
+
+ accentaccent notificationUntil dismissed +
+
+ featurefeature notificationUntil dismissed +
+
+ secondarysecondary notificationUntil dismissed +
+
+ customcustom notificationUntil dismissed +
+
+ ghostghost notificationUntil dismissed +
+
+ infoinfo notification5 seconds +
+
+ successsuccess notification5 seconds +
+
+ warningwarning notification10 seconds +
+
+ dangerdanger notificationUntil dismissed +
+
## Notification lifecycle @@ -91,7 +157,11 @@ Mount one host per store. ToastHost positions notices over the page and provides

ToastHost contract

-

Props below are the public template API. RefOrValue accepts a literal or a reactive ref; bind refs with a colon-prefixed attribute. Native attributes and events can be passed through the component root.

+

+ Props below are the public template API. RefOrValue accepts a literal or a + reactive ref; bind refs with a colon-prefixed attribute. Native attributes + and events can be passed through the component root. +

- -

ToastStore.items: a shallow ref of ToastItem[] with id, message and SemanticTone.

+ +

+ ToastStore.items: a shallow ref of ToastItem[] with id, message and + SemanticTone. +

- -

ToastStore.dismiss callback. Receives the ID of the notification to remove.

+ +

+ ToastStore.dismiss callback. Receives the ID of the notification to remove. +

### Composition
-

No slots. Register defineToastComponents with Flex, Panel, Btn and Icon dependencies. Include iconoir:xmark in the icon resolver.

+

+ No slots. Register defineToastComponents with Flex, Panel, Btn and Icon + dependencies. Include iconoir:xmark in the icon resolver. +

### ToastStore diff --git a/frontend/purestack.studio/components/data/bar-chart/bar-chart.mdx b/frontend/purestack.studio/components/data/bar-chart/bar-chart.mdx index c68cbfc6..650f5561 100644 --- a/frontend/purestack.studio/components/data/bar-chart/bar-chart.mdx +++ b/frontend/purestack.studio/components/data/bar-chart/bar-chart.mdx @@ -13,26 +13,41 @@ nav: # BarChart - + ## Playground Change the dataset, edit each daily value, and see the chart update immediately. Try an all-negative balance, set scale bounds, switch colors and units, or remove the data entirely. Reset restores every control. - + - - - - - + + + + ```html ``` - + @@ -40,22 +55,27 @@ Change the dataset, edit each daily value, and see the chart update immediately. Use explicit bounds to compare values against a stable range. Here, both categories use a zero-to-100 scale. Bounds expand when needed to include the data; they do not clip outlying values. - + -
- - -
-
+
+ +
+ ```html + -
- - -
-
+
+ +
+
```html + -
- -

76 deployments this week

-

Daily activity across the release pipeline.

- -
- -
-
+
+ +

76 deployments this week

+

Daily activity across the release pipeline.

+ +
+
+
```html @@ -154,51 +184,103 @@ Hide the grid and numeric labels when the shape is the useful signal. Keep an ac Compare all ten tones together. These examples use tone-aware plot colors; custom data colors and the chart’s tone remain independent. - -
+ +
+ +
## All eighteen visual variants Every supported treatment is visible in accent, including none. The default chart variant is none; the live editor lets you combine any tone and treatment. - -
+ +
+ +
## Both variant modes Stateless keeps the resting appearance. Stateful adds the treatment’s hover and active classes; hover the outlineFill examples to compare them. Charts default to stateless and retain their native plot hover feedback in either mode. - -
+ +
+ +
## Values, labels, and axes Compare all display switches together. Empty data has its own message; signed and zero datasets are also available in the live editor. - -
-
All annotations - -
-
No values - -
-
No category labels - -
-
No axes - -
-
All zero - -
-
Empty - -
-
+ +
+
+ All annotations + +
+
+ No values + +
+
+ No category labels + +
+
+ No axes + +
+
+ All zero + +
+
+ Empty + +
+
## Feature map @@ -222,7 +304,11 @@ Each valid item is a category. Non-finite values are removed; negative values ex

BarChart contract

-

Props below are the public template API. RefOrValue accepts a literal or a reactive ref; bind refs with a colon-prefixed attribute. Native attributes and events can be passed through the component root.

+

+ Props below are the public template API. RefOrValue accepts a literal or a + reactive ref; bind refs with a colon-prefixed attribute. Native attributes + and events can be passed through the component root. +

- -

Array of BarChartItem objects with optional label, numeric value (or numeric string), and CSS color. Missing labels become Item 1, Item 2 and so on.

+ +

+ Array of BarChartItem objects with optional label, numeric value (or numeric + string), and CSS color. Missing labels become Item 1, Item 2 and so on. +

- +

SVG title text; not a separate visible heading.

- +

SVG desc text explaining the data or takeaway.

- +

Accessible name on the SVG image.

- +

Visible fallback when no usable data remains.

- +

Unit appended to formatted values, such as ms or %.

- -

CSS width. Numeric values become pixel lengths; strings can use CSS units.

+ +

+ CSS width. Numeric values become pixel lengths; strings can use CSS units. +

- -

CSS chart height; omitted height preserves the SVG’s intrinsic aspect ratio.

+ +

+ CSS chart height; omitted height preserves the SVG’s intrinsic aspect ratio. +

- -

Requested lower domain bound. The resolved minimum also includes zero and the smallest data value. Omit it to use the data range automatically.

+ +

+ Requested lower domain bound. The resolved minimum also includes zero and + the smallest data value. Omit it to use the data range automatically. +

- -

Requested upper domain bound. The resolved maximum also includes zero and the largest data value, so bars are never clipped by a tighter bound.

+ +

+ Requested upper domain bound. The resolved maximum also includes zero and + the largest data value, so bars are never clipped by a tighter bound. +

- -

Enables the SVG entrance animation when bars mount. Bind false for immediate rendering, especially while editing values. This is not an interpolated transition between datasets.

+ +

+ Enables the SVG entrance animation when bars mount. Bind false for immediate + rendering, especially while editing values. This is not an interpolated + transition between datasets. +

- -

Draws numeric values beside marks. Enable only when labels have enough room.

+ +

+ Draws numeric values beside marks. Enable only when labels have enough room. +

- +

Shows category labels along the horizontal axis.

- +

Shows the axis grid and value labels.

- -

Semantic intent: neutral, accent, secondary, info, success, warning, danger, feature, custom or ghost. The active skin supplies the colors.

+ +

+ Semantic intent: neutral, accent, secondary, info, success, warning, danger, + feature, custom or ghost. The active skin supplies the colors. +

- -

Visual treatment. Accepts solid, surface, surfaceAlt, spotlight, glass, flat, flatAlt, flatSolid, outlineFill, outline, subtle, subtleBtn, link, sheen, underline, rail, bracket or none.

+ +

+ Visual treatment. Accepts solid, surface, surfaceAlt, spotlight, glass, + flat, flatAlt, flatSolid, outlineFill, outline, subtle, subtleBtn, link, + sheen, underline, rail, bracket or none. +

- -

Use stateless for content surfaces; stateful enables the treatment’s hover, focus and active styles. It does not add interaction handlers.

+ +

+ Use stateless for content surfaces; stateful enables the treatment’s hover, + focus and active styles. It does not add interaction handlers. +

### Composition
-

No slots. Register defineBarChartComponents. Bind a ref of BarChartItem[] to update the data.

+

+ No slots. Register defineBarChartComponents. Bind a ref of BarChartItem[] to + update the data. +

## Related components diff --git a/frontend/purestack.studio/components/data/bar-chart/playground.ts b/frontend/purestack.studio/components/data/bar-chart/playground.ts index 1f7c3da6..050ab83c 100644 --- a/frontend/purestack.studio/components/data/bar-chart/playground.ts +++ b/frontend/purestack.studio/components/data/bar-chart/playground.ts @@ -64,16 +64,43 @@ export interface BarChartPlayground extends ChartAppearanceState { const barChartPlaygroundTemplate = html` - - - + + + - +

LIVE PREVIEW · WEEKLY BALANCE

-
+
:showAxis="axisVisible" :showLabels="labelsVisible" :animated="motionEnabled" - style="min-width: 440px" - /> + style="min-width: 440px"/>
{{ summary }} - - - - + + + +

Edit the data

@@ -112,15 +147,34 @@ const barChartPlaygroundTemplate = html` :label="row.label" type="number" step="1" - :model="row.value" - /> + :model="row.value"/> - - - + + + -

Blank bounds use the data range. The scale always includes zero and every value. On narrow screens, scroll the preview horizontally to keep its labels readable.

+

+ Blank bounds use the data range. The scale always includes zero and every + value. On narrow screens, scroll the preview horizontally to keep its labels + readable. +

` const presetValues: Record = { @@ -255,5 +309,5 @@ createApp( ...definePanelComponents(), }, }, - { selector: 'app#bar-chart-demo', template: html`` }, + { selector: 'app#bar-chart-demo', template: html`` }, ) diff --git a/frontend/purestack.studio/components/data/doughnut-chart/doughnut-chart.mdx b/frontend/purestack.studio/components/data/doughnut-chart/doughnut-chart.mdx index 1982afba..e1821151 100644 --- a/frontend/purestack.studio/components/data/doughnut-chart/doughnut-chart.mdx +++ b/frontend/purestack.studio/components/data/doughnut-chart/doughnut-chart.mdx @@ -13,26 +13,41 @@ nav: # DoughnutChart - + ## Interactive playground Edit segment names and values, hide a category, and watch the total and percentage shares recalculate. Try **Tiny slices** with a larger gap, **One positive segment**, or **All zero** to explore the edge cases. Entrance animation is enabled; use Replay to see it again. - + - - - - - + + + + ```html ``` - + @@ -42,34 +57,44 @@ The legend, percentage calculations and center modes are composed in the example Pair the ring with a visible legend and a short interpretation. The automatic center value is the sum of the positive segments. - + -
- -

RELEASE READINESS

-

A clear picture of the sprint

- - - - Complete24 · 60% - In review10 · 25% - Planned6 · 15% -

60% of the sprint is complete. Review is the next place to focus.

-
-
-
- -
-
+
+ +

RELEASE READINESS

+

A clear picture of the sprint

+ + + + Complete24 · 60% + In review10 · 25% + Planned6 · 15% +

+ 60% of the sprint is complete. Review is the next place to focus. +

+
+
+
+
+ ```html @@ -102,36 +127,44 @@ Pair the ring with a visible legend and a short interpretation. The automatic ce Pass actual counts as segments and compute the percentage separately. Setting centerValue to 75% does not turn a single value of 18 into a progress ring; include the remaining 6 as a second segment. - + -
- - - - - Release checklist -

18 of 24 checks complete

-

6 checks remain before the release is ready.

-

The center shows completion. The segments still contain the actual counts.

-
-
-
- -
-
+
+ + + + + Release checklist +

18 of 24 checks complete

+

6 checks remain before the release is ready.

+

+ The center shows completion. The segments still contain the actual + counts. +

+
+
+
+
+
```html @@ -166,51 +199,55 @@ Pass actual counts as segments and compute the percentage separately. Setting ce Compare a thin, continuous ring with a thicker ring containing very small shares. Gaps adapt to adjacent slices, so a large requested gap does not erase a tiny category. Keep exact values visible alongside the chart. - + -
- - -

Continuous ring

- -

Media 60 GB · Documents 25 GB · Backups 15 GB

-
- -

Tiny shares

- -

Stable 99.5% · Canary 0.4% · Preview 0.1%

-
-
- -
-
+
+ + +

Continuous ring

+ +

Media 60 GB · Documents 25 GB · Backups 15 GB

+
+ +

Tiny shares

+ +

Stable 99.5% · Canary 0.4% · Preview 0.1%

+
+
+
+
```html @@ -260,40 +297,50 @@ Compare a thin, continuous ring with a thicker ring containing very small shares A single positive segment becomes a full ring. An empty array and an array of all-zero values both have no positive total. - + -
- - -

One category

- -

24 passed. One positive segment fills the entire ring, with no separator.

-
- -

Awaiting data

- -

No results yet. Leave both center props unset to display the empty label.

-
-
- -
-
+
+ + +

One category

+ +

+ 24 passed. One positive segment fills the entire ring, with no + separator. +

+
+ +

Awaiting data

+ +

+ No results yet. Leave both center props unset to display the empty + label. +

+
+
+
+
```html @@ -332,57 +379,132 @@ A single positive segment becomes a full ring. An empty array and an array of al Compare all ten tones together. These examples use tone-aware plot colors; custom data colors and the chart’s tone remain independent. - -
+ +
+ +
## All eighteen visual variants Every supported treatment is visible in accent, including none. The default chart variant is none; the live editor lets you combine any tone and treatment. - -
+ +
+ +
## Both variant modes Stateless keeps the resting appearance. Stateful adds the treatment’s hover and active classes; hover the outlineFill examples to compare them. Charts default to stateless and retain their native plot hover feedback in either mode. - -
+ +
+ +
## Ring geometry and center copy Thin and thick rings, joined and separated slices, rotation, automatic totals, and custom center text are all shown together. Only finite positive values contribute. - -
-
Thin ring - -
-
Thick ring - -
-
No gap - -
-
Separated slices - -
-
Rotated - -
-
Custom center - -
-
One full-circle segment - -
-
Empty - -
-
+ +
+
+ Thin ring + +
+
+ Thick ring + +
+
+ No gap + +
+
+ Separated slices + +
+
+ Rotated + +
+
+ Custom center + +
+
+ One full-circle segment + +
+
+ Empty + +
+
## Feature map @@ -410,7 +532,11 @@ Thin and thick rings, joined and separated slices, rotation, automatic totals, a

DoughnutChart contract

-

Props below are the public template API. RefOrValue accepts a literal or a reactive ref; bind refs with a colon-prefixed attribute. Native attributes and events can be passed through the component root.

+

+ Props below are the public template API. RefOrValue accepts a literal or a + reactive ref; bind refs with a colon-prefixed attribute. Native attributes + and events can be passed through the component root. +

+
```html @@ -98,23 +118,28 @@ Give each series the same ordered categories and a visible legend. A fixed zero- Area fill closes each path against zero. Positive and negative regions keep their direction, while value labels show the actual observations. Multiple series would overlap; area fill is not stacking. - + -
- - -
-
+
+ +
+
```html + -
- -

ILLUSTRATIVE LATENCY

-

18 ms latest measurement

-

Six observations across a tuning session.

- -
- -
-
+
+ +

ILLUSTRATIVE LATENCY

+

18 ms latest measurement

+

Six observations across a tuning session.

+ +
+
+
```html @@ -184,35 +214,39 @@ Use a nearby headline to explain a small trend. Remove the axis, category labels One observation needs a marker because there is no line segment yet. An empty series list displays your emptyLabel and removes the chart marks. Neither case needs placeholder data. - + -
- - -

First measurement

- -
- -

Awaiting measurements

- -
-
- -
-
+
+ + +

First measurement

+ +
+ +

Awaiting measurements

+ +
+
+
+
```html @@ -246,54 +280,122 @@ One observation needs a marker because there is no line segment yet. An empty se Compare all ten tones together. These examples use tone-aware plot colors; custom data colors and the chart’s tone remain independent. - -
+ +
+ +
## All eighteen visual variants Every supported treatment is visible in accent, including none. The default chart variant is none; the live editor lets you combine any tone and treatment. - -
+ +
+ +
## Both variant modes Stateless keeps the resting appearance. Stateful adds the treatment’s hover and active classes; hover the outlineFill examples to compare them. Charts default to stateless and retain their native plot hover feedback in either mode. - -
+ +
+ +
## Curves and display layers Every layer is visible here without changing a toggle. Smooth interpolation is a visual treatment of equally spaced observations; it does not add observations to the data. - -
-
Linear - -
-
Smooth - -
-
Area - -
-
Points and values - -
-
Only the line - -
-
One observation - -
-
Empty - -
-
+ +
+
+ Linear + +
+
+ Smooth + +
+
+ Area + +
+
+ Points and values + +
+
+ Only the line + +
+
+ One observation + +
+
+ Empty + +
+
## Feature map @@ -321,7 +423,11 @@ Every layer is visible here without changing a toggle. Smooth interpolation is a

LineChart contract

-

Props below are the public template API. RefOrValue accepts a literal or a reactive ref; bind refs with a colon-prefixed attribute. Native attributes and events can be passed through the component root.

+

+ Props below are the public template API. RefOrValue accepts a literal or a + reactive ref; bind refs with a colon-prefixed attribute. Native attributes + and events can be passed through the component root. +

@@ -313,8 +293,7 @@ card.toCSS() class="tone-sample" aria-label="Semantic tones: accent, info, success, warning, danger" > - One palette.
Every component.
+ One palette.
Every component.
toCSS() summary="Render content to HTML. Mount Regor apps where interaction belongs. Bundle browser TypeScript through the same pipeline." >
- HTMLCSS+ your JSWEB + HTMLCSS+ your JSWEB
toCSS() summary="Component completion, typed prop hints, template diagnostics, and go-to-definition with the PureStack VS Code extension." >
- <Panel + <Panel tone="success">SemanticTone + >"success">SemanticTone
Install the VS Code extension + variant="link" + >Install the VS Code extension
-
+
toCSS() variant="link" icon="tabler:arrow-up-right" iconPosition="end" - >Read the philosophy + >Read the philosophy
- M↓ ContentTS ComponentsTS StylesTS Scripts + M↓ ContentTS ComponentsTS StylesTS Scripts + >
- PureStackOne coherent source model + PureStackOne coherent source model
- Your product frontend + Your product frontend

Readable by humans. Navigable by AI.

@@ -439,8 +411,9 @@ card.toCSS()

Small signals. A whole frontend.

Meet the reactive TypeScript library at PureStack's core. Regor connects - state to HTML and SVG, composes components, and updates the DOM directly. - The same component model renders your pages and powers your browser apps. + state to HTML and SVG, composes components, and updates the DOM + directly. The same component model renders your pages and powers your + browser apps.

toCSS() variant="solid" icon="tabler:arrow-up-right" iconPosition="end" - >Explore Regor - Regor in PureStack + >Explore Regor + Regor in PureStack @@ -492,8 +460,7 @@ card.toCSS() Frontend infrastructure. Your backend, your - hosting.toCSS() variant="link" icon="tabler:arrow-up-right" iconPosition="end" - >View all packages + >View all packages
@@ -522,8 +488,8 @@ card.toCSS() 03 / START BUILDING

Make something
of your own.

- PureStack CLI opens PureStack Forge, PureStack's website builder. - Start from a template, create your site, and make it yours. + PureStack CLI opens PureStack Forge, PureStack's website builder. Start + from a template, create your site, and make it yours.

  1. @@ -544,15 +510,16 @@ card.toCSS() 03
    Create it. See it. Make it yours. -

    Browse your new site. Your content and code are yours to shape.

    +

    + Browse your new site. Your content and code are yours to shape. +

- Your terminalCLI → PURESTACK FORGE + Your terminalCLI → PURESTACK FORGE
toCSS() Choose. Create. Browse your live site.
- Your first site, step by step + Your first site, step by step
@@ -609,7 +573,8 @@ card.toCSS() Yes. Regor renders components and directives in MDX to static HTML during the build, and powers interactive apps in the browser. Your own components can work in both places; browser apps register the built-in and custom - components they use. See the Regor guide. + components they use. + See the Regor guide. No. PureStack produces static HTML, CSS, and browser assets that you can @@ -618,14 +583,18 @@ card.toCSS() Run npx purestack@latest init and open the link in your - terminal. PureStack Forge guides you through configuring and creating your site, - including optional dependency installation. Follow the - CLI guide for your first steps. + terminal. PureStack Forge guides you through configuring and creating your + site, including optional dependency installation. Follow the + CLI guide for your + first steps. -
+
@@ -642,10 +611,8 @@ card.toCSS() variant="solid" icon="tabler:arrow-up-right" iconPosition="end" - >Build with PureStackOpen source. TypeScript-native. Yours to build on. + >Build with PureStackOpen source. TypeScript-native. Yours to build on.
diff --git a/package.json b/package.json index e47703d8..56f3e827 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "purestack-repo", - "version": "1.1.23", + "version": "1.1.24", "packageManager": "yarn@4.18.1", "type": "module", "sideEffects": false, @@ -62,7 +62,9 @@ "verify-toolchain": "tsx scripts/verify-toolchain.ts", "pack-vscode-ext": "yarn --cwd packages/ts-ssg-vscode build && yarn --cwd packages/ts-ssg-vscode package", "longest": "yarn tsx ./scripts/longest-functions.ts", - "format": "biome format --write", + "format": "biome format --write && yarn format:markup --write", + "format:markup": "tsx scripts/format-markup.ts", + "format:check": "biome format && yarn format:markup --check", "pre-commit": "yarn check-dependencies && yarn check-eol && yarn regor-components && yarn embed && yarn format && yarn lint && yarn build && yarn test run" } } diff --git a/packages/purestack/forge/footer.mdx b/packages/purestack/forge/footer.mdx index 83e7a6d0..dd74ed10 100644 --- a/packages/purestack/forge/footer.mdx +++ b/packages/purestack/forge/footer.mdx @@ -11,13 +11,11 @@ href="https://purestack.studio/guides/" target="_blank" rel="noopener noreferrer" - >Documentation ↗ + >Documentation ↗ GitHub ↗ + >GitHub ↗ diff --git a/packages/purestack/forge/header.mdx b/packages/purestack/forge/header.mdx index d831693a..01d47e9a 100644 --- a/packages/purestack/forge/header.mdx +++ b/packages/purestack/forge/header.mdx @@ -1 +1 @@ - + diff --git a/packages/purestack/forge/index.mdx b/packages/purestack/forge/index.mdx index a3a9c1fc..b3be77b1 100644 --- a/packages/purestack/forge/index.mdx +++ b/packages/purestack/forge/index.mdx @@ -17,7 +17,5 @@ index: false subtitle="Choose a starting point and make the project your own. Move between sections at any time." class="mb-6"/> - + diff --git a/packages/purestack/forge/modules/components/creation-panel.ts b/packages/purestack/forge/modules/components/creation-panel.ts index d36fecba..addf84af 100644 --- a/packages/purestack/forge/modules/components/creation-panel.ts +++ b/packages/purestack/forge/modules/components/creation-panel.ts @@ -7,12 +7,18 @@ export interface CreationPanel { } const creationPanelTemplate = html`
- - +
` export function defineCreationPanelComponent() { diff --git a/packages/purestack/forge/modules/components/creation-progress.ts b/packages/purestack/forge/modules/components/creation-progress.ts index 9bcc563e..8aeca8ac 100644 --- a/packages/purestack/forge/modules/components/creation-progress.ts +++ b/packages/purestack/forge/modules/components/creation-progress.ts @@ -16,12 +16,23 @@ export interface CreationProgress { const creationProgressTemplate = html` -
{{ currentLabel }}

{{ completed }} of {{ tasks.length }} steps finished

+
+ {{ currentLabel }} +

+ {{ completed }} of {{ tasks.length }} steps finished +

+
- +
  1. - + {{ task.label }}{{ task.state === 'running' ? 'In progress' : task.state === 'done' ? 'Complete' : task.state === 'failed' ? 'Needs attention' : 'Waiting' }}
diff --git a/packages/purestack/forge/modules/components/creation-result.ts b/packages/purestack/forge/modules/components/creation-result.ts index 0458cee0..3d236339 100644 --- a/packages/purestack/forge/modules/components/creation-result.ts +++ b/packages/purestack/forge/modules/components/creation-result.ts @@ -14,14 +14,31 @@ const creationResultTemplate = html`

{{ project.directory }}

{{ project.template }}

- {{ failure }} - {{ project.previewError }} + {{ failure }} + {{ project.previewError }} - Browse live site ↗ - Start live site - Finish Forge + Browse live site ↗ + Start live site + Finish Forge -

Forge stays open while you explore. Source edits appear on the live site automatically. Finishing Forge stops both local servers.

+

+ Forge stays open while you explore. Source edits appear on the live site + automatically. Finishing Forge stops both local servers. +

` diff --git a/packages/purestack/forge/modules/components/dashboard.ts b/packages/purestack/forge/modules/components/dashboard.ts index 5a1a4aa6..f5aac347 100644 --- a/packages/purestack/forge/modules/components/dashboard.ts +++ b/packages/purestack/forge/modules/components/dashboard.ts @@ -40,11 +40,17 @@ export class Dashboard { } } -const dashboardTemplate = html` +const dashboardTemplate = html` - {{ model.error }} + {{ model.error }}
diff --git a/packages/purestack/forge/modules/components/navigation.ts b/packages/purestack/forge/modules/components/navigation.ts index 2842eb54..95c561fc 100644 --- a/packages/purestack/forge/modules/components/navigation.ts +++ b/packages/purestack/forge/modules/components/navigation.ts @@ -6,19 +6,41 @@ export interface Navigation { workspace: ProjectModel } -const navigationTemplate = html` +const navigationTemplate = html`

YOUR NEW WEBSITE

- - + + :aria-current="!workspace.showCreation && workspace.section === entry.id ? 'step' : null" + :disabled="workspace.showCreation" + @click="workspace.select(entry.id)" + > {{ entry.number }}{{ entry.label }}

LOCAL SESSION

-

{{ workspace.connection === 'connected' ? 'Connected to your workspace' : workspace.connection === 'closed' ? 'Session closed' : 'Connecting to your workspace…' }}

-

{{ workspace.cwd }}

+

+ {{ workspace.connection === 'connected' ? 'Connected to your workspace' : workspace.connection === 'closed' ? 'Session closed' : 'Connecting to your workspace…' }} +

+

+ {{ workspace.cwd }} +

` diff --git a/packages/purestack/forge/modules/components/next-steps.ts b/packages/purestack/forge/modules/components/next-steps.ts index d6af9f98..14815cab 100644 --- a/packages/purestack/forge/modules/components/next-steps.ts +++ b/packages/purestack/forge/modules/components/next-steps.ts @@ -6,9 +6,15 @@ export interface NextSteps { const nextStepsTemplate = html`

Keep building from your terminal

-

After finishing Forge, run these commands to start the site again.

-
{{ commands.join('\n') }}
-

README.md and AGENTS.md guide your next changes.

+

+ After finishing Forge, run these commands to start the site again. +

+
{{ commands.join('\n') }}
+

+ README.md and AGENTS.md guide your next changes. +

` export function defineNextStepsComponent() { diff --git a/packages/purestack/forge/modules/components/option-field.ts b/packages/purestack/forge/modules/components/option-field.ts index 1c44a744..6dbccae5 100644 --- a/packages/purestack/forge/modules/components/option-field.ts +++ b/packages/purestack/forge/modules/components/option-field.ts @@ -14,12 +14,24 @@ export interface OptionField { } const optionFieldTemplate = html`
- - - + + +

{{ option.field.description }}

` diff --git a/packages/purestack/forge/modules/components/options-step.ts b/packages/purestack/forge/modules/components/options-step.ts index 88782fdd..bbeef877 100644 --- a/packages/purestack/forge/modules/components/options-step.ts +++ b/packages/purestack/forge/modules/components/options-step.ts @@ -6,15 +6,33 @@ export interface OptionsStep { workspace: ProjectModel } -const optionsStepTemplate = html` -

Loading template settings…

-

This template is ready to use with no additional settings.

+const optionsStepTemplate = html` +

+ Loading template settings… +

+

+ This template is ready to use with no additional settings. +

- +
` diff --git a/packages/purestack/forge/modules/components/review-step.ts b/packages/purestack/forge/modules/components/review-step.ts index fe13fd65..67c1d5f5 100644 --- a/packages/purestack/forge/modules/components/review-step.ts +++ b/packages/purestack/forge/modules/components/review-step.ts @@ -6,16 +6,29 @@ export interface ReviewStep { workspace: ProjectModel } -const reviewStepTemplate = html` +const reviewStepTemplate = html`
- +
-

Your source files will be created in the selected folder. Forge will start a live site and stay open.

+

+ Your source files will be created in the selected folder. Forge will start a + live site and stay open. +

` diff --git a/packages/purestack/forge/modules/components/setup-step.ts b/packages/purestack/forge/modules/components/setup-step.ts index 6c6e7e5f..85f72b4f 100644 --- a/packages/purestack/forge/modules/components/setup-step.ts +++ b/packages/purestack/forge/modules/components/setup-step.ts @@ -6,20 +6,41 @@ export interface SetupStep { workspace: ProjectModel } -const setupStepTemplate = html` +const setupStepTemplate = html`
- -

Use the package manager configured by the template.

+ +

+ Install dependencies, then format your project if the template provides + a format script. +

- -

Create a repository, or reuse an existing one.

+ +

+ Create a repository, or reuse an existing one. +

` diff --git a/packages/purestack/forge/modules/components/site-step.ts b/packages/purestack/forge/modules/components/site-step.ts index 7e1f9e1a..2a7bce06 100644 --- a/packages/purestack/forge/modules/components/site-step.ts +++ b/packages/purestack/forge/modules/components/site-step.ts @@ -6,16 +6,35 @@ export interface SiteStep { workspace: ProjectModel } -const siteStepTemplate = html` +const siteStepTemplate = html` - +

The name people see on your website.

- -

Relative to the folder where you started the CLI.

+ +

+ Relative to the folder where you started the CLI. +

` diff --git a/packages/purestack/forge/modules/components/step-actions.ts b/packages/purestack/forge/modules/components/step-actions.ts index 8a0b25a6..731c31da 100644 --- a/packages/purestack/forge/modules/components/step-actions.ts +++ b/packages/purestack/forge/modules/components/step-actions.ts @@ -9,10 +9,14 @@ export interface StepActions { disabled?: RefOrValue } -const stepActionsTemplate = html` +const stepActionsTemplate = html` ← Back - {{ nextLabel }} → + {{ nextLabel }} → ` export function defineStepActionsComponent() { diff --git a/packages/purestack/forge/modules/components/step.ts b/packages/purestack/forge/modules/components/step.ts index 90b3a3b5..f1b24d03 100644 --- a/packages/purestack/forge/modules/components/step.ts +++ b/packages/purestack/forge/modules/components/step.ts @@ -8,7 +8,11 @@ export interface StepPanel { } const stepTemplate = html`
- +
` diff --git a/packages/purestack/forge/modules/components/template-card.ts b/packages/purestack/forge/modules/components/template-card.ts index c9862842..c1ae05be 100644 --- a/packages/purestack/forge/modules/components/template-card.ts +++ b/packages/purestack/forge/modules/components/template-card.ts @@ -7,14 +7,27 @@ export interface TemplateCard { choose: (id: string) => void } -const templateCardTemplate = html` +const templateCardTemplate = html` - +

{{ entry.title }}

{{ entry.description }}

- + {{ selected ? 'Selected' : 'Choose template' }}
diff --git a/packages/purestack/forge/modules/components/templates-step.ts b/packages/purestack/forge/modules/components/templates-step.ts index 53c86c06..870773fa 100644 --- a/packages/purestack/forge/modules/components/templates-step.ts +++ b/packages/purestack/forge/modules/components/templates-step.ts @@ -6,18 +6,40 @@ export interface TemplatesStep { workspace: ProjectModel } -const templatesStepTemplate = html` +const templatesStepTemplate = html` - + - - {{ workspace.templateLoading ? 'Loading template…' : 'Load template' }} + + {{ workspace.templateLoading ? 'Loading template…' : 'Load template' }} ` diff --git a/packages/purestack/package.json b/packages/purestack/package.json index 4ba6c8ec..98a2a82c 100644 --- a/packages/purestack/package.json +++ b/packages/purestack/package.json @@ -1,6 +1,6 @@ { "name": "purestack", - "version": "1.1.23", + "version": "1.1.24", "packageManager": "yarn@4.9.2", "module": "./dist/purestack.mjs", "types": "./dist/purestack.d.mts", @@ -45,6 +45,7 @@ }, "dependencies": { "@purestack/ts-components": "workspace:^", + "@purestack/ts-format": "workspace:^", "@purestack/ts-ssg": "workspace:^", "@purestack/ts-style": "workspace:^", "@purestack/ts-svg-icons": "workspace:^", diff --git a/packages/purestack/src/cli/application.ts b/packages/purestack/src/cli/application.ts index 95ddfa66..03aea5c4 100644 --- a/packages/purestack/src/cli/application.ts +++ b/packages/purestack/src/cli/application.ts @@ -16,6 +16,7 @@ const help = `PureStack purestack serve --content Preview with live reload purestack build --content Build static files purestack publish --content Prepare a local release artifact + purestack format [paths] --write Format PureStack markup and templates purestack skills update Refresh this site's AI agent skill purestack --version Print the installed version @@ -31,9 +32,17 @@ export async function runApplication( return 0 } if (!args.length || ['--help', '-h', 'help'].includes(args[0])) { - console.log(args[0] === 'help' && args[1] === 'init' ? initHelp : help) + if (args[0] === 'help' && args[1] === 'format') { + const { formatHelp } = await import('./format') + console.log(formatHelp) + } else + console.log(args[0] === 'help' && args[1] === 'init' ? initHelp : help) return 0 } + if (args[0] === 'format') { + const { runFormatCli } = await import('./format') + return await runFormatCli(args.slice(1), environment.cwd) + } if (args[0] === 'skills') { if (args[1] !== 'update' || args.length > 2) throw new Error('Usage: purestack skills update') diff --git a/packages/purestack/src/cli/forge/session.test.ts b/packages/purestack/src/cli/forge/session.test.ts index b24c86ef..4f6b3cf7 100644 --- a/packages/purestack/src/cli/forge/session.test.ts +++ b/packages/purestack/src/cli/forge/session.test.ts @@ -13,12 +13,13 @@ import type { DevWebSocketConnection, DevWebSocketMessage, } from '@purestack/ts-ssg' -import { describe, expect, it } from 'vitest' +import { describe, expect, it, vi } from 'vitest' import type { SessionCommand, SessionEvent, } from '../../../forge/modules/shared/protocol' import { createSite } from '../../init/create-site' +import * as managers from '../../init/package-manager' import { readTemplateUi } from '../../init/template-ui' import { parseInitArguments } from '../arguments' import { Session } from './session' @@ -122,6 +123,51 @@ async function fixture(previewFails = false) { } describe('Forge session', () => { + it('streams formatting progress after installation and before starting the live site', async () => { + const test = await fixture() + const prepare = vi + .spyOn(managers, 'preparePackageManager') + .mockResolvedValue(undefined) + const run = vi.spyOn(managers, 'runPackageManager').mockResolvedValue('') + test.gate.resolve() + try { + test.peer.request({ + type: 'create', + configuration: { + ...test.session.snapshot.configuration, + install: true, + }, + }) + await expect.poll(() => test.session.snapshot.status).toBe('ready') + const tasks = test.session.snapshot.tasks + const labels = tasks.map((task) => task.label) + expect(labels.indexOf('Format project files')).toBeGreaterThan( + labels.indexOf('Install dependencies'), + ) + expect(labels.indexOf('Format project files')).toBeLessThan( + labels.indexOf('Start live site'), + ) + expect(tasks.every((task) => task.state === 'done')).toBe(true) + expect( + test.peer.events.some( + (event) => + event.type === 'snapshot' && + event.snapshot.tasks.some( + (task) => + task.label === 'Format project files' && + task.state === 'running', + ), + ), + ).toBe(true) + expect(test.session.snapshot.result?.failures).toEqual([]) + expect(test.counts().previews).toBe(1) + } finally { + await test.close() + prepare.mockRestore() + run.mockRestore() + } + }) + it('creates once, streams tasks, survives reconnects and keeps the live site open until finishing', async () => { const test = await fixture() try { diff --git a/packages/purestack/src/cli/format.test.ts b/packages/purestack/src/cli/format.test.ts new file mode 100644 index 00000000..193c6d2a --- /dev/null +++ b/packages/purestack/src/cli/format.test.ts @@ -0,0 +1,29 @@ +import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import path from 'node:path' +import { afterEach, describe, expect, it, vi } from 'vitest' +import { runFormatCli } from './format' + +afterEach(() => vi.restoreAllMocks()) + +describe('purestack format', () => { + it('checks by default, writes on request, and returns correct exit statuses', async () => { + vi.spyOn(console, 'log').mockImplementation(() => {}) + const cwd = await mkdtemp(path.join(tmpdir(), 'purestack-format-cli-')) + try { + const file = path.join(cwd, 'page.mdx') + const source = '' + await writeFile(file, source) + expect(await runFormatCli([], cwd)).toBe(1) + expect(await readFile(file, 'utf8')).toBe(source) + expect(await runFormatCli(['page.mdx', '--write'], cwd)).toBe(0) + expect(await readFile(file, 'utf8')).toBe('') + expect(await runFormatCli(['--check'], cwd)).toBe(0) + await expect(runFormatCli(['--write', '--check'], cwd)).rejects.toThrow( + 'Choose either', + ) + } finally { + await rm(cwd, { recursive: true, force: true }) + } + }) +}) diff --git a/packages/purestack/src/cli/format.ts b/packages/purestack/src/cli/format.ts new file mode 100644 index 00000000..5d09ef22 --- /dev/null +++ b/packages/purestack/src/cli/format.ts @@ -0,0 +1,65 @@ +import path from 'node:path' +import { parseArgs } from 'node:util' +import { + type FormatFilesOptions, + formatFiles, +} from '@purestack/ts-format/files' + +export const formatHelp = `Usage: purestack format [files or directories] [options] + +Format PureStack component markup in .mdx/.rmdx and html/svg templates in TypeScript. +Markdown prose, frontmatter, and TypeScript outside those templates stay untouched. +Defaults to checking the current folder. Git-ignored and generated directories are skipped. + + --write Save formatted files + --check Check without writing (default); exit with 1 if changes are needed + -h, --help Show help + +Examples: + purestack format content --write + purestack format content --check` + +export async function runFormatCli( + args: string[], + cwd: string, + options: FormatFilesOptions = {}, +) { + const { values, positionals } = parseArgs({ + args, + allowPositionals: true, + options: { + write: { type: 'boolean', default: false }, + check: { type: 'boolean', default: false }, + help: { type: 'boolean', short: 'h', default: false }, + }, + }) + if (values.help) { + console.log(formatHelp) + return 0 + } + if (values.write && values.check) + throw new Error('Choose either --write or --check.') + const result = await formatFiles(positionals.length ? positionals : ['.'], { + ...options, + cwd, + write: values.write, + }) + for (const file of result.changed) { + console.log( + `${values.write ? 'Formatted' : 'Needs formatting:'} ${path.relative(cwd, file)}`, + ) + } + console.log( + `Checked ${result.checked} files; ${result.changed.length} ${values.write ? 'formatted' : 'need formatting'}.`, + ) + if (result.errors.length) { + console.error( + `\nFormatting failed for ${new Set(result.errors.map((error) => error.filePath)).size} files:`, + ) + for (const error of result.errors) + console.error(`${path.relative(cwd, error.filePath)}: ${error.message}`) + } + return result.errors.length || (!values.write && result.changed.length) + ? 1 + : 0 +} diff --git a/packages/purestack/src/index.ts b/packages/purestack/src/index.ts index ddfe3ff4..84647ef2 100644 --- a/packages/purestack/src/index.ts +++ b/packages/purestack/src/index.ts @@ -1,3 +1,3 @@ -export const version: string = '1.1.23' +export const version: string = '1.1.24' export * from '@purestack/ts-ssg' diff --git a/packages/purestack/src/init/create-site.ts b/packages/purestack/src/init/create-site.ts index 6c8dd830..29d21f7b 100644 --- a/packages/purestack/src/init/create-site.ts +++ b/packages/purestack/src/init/create-site.ts @@ -67,6 +67,7 @@ export async function createSite( const temporary = await mkdtemp(path.join(tmpdir(), 'purestack-init-')) let template: string let packageManager: PackageManager + let hasFormatScript: boolean try { const unpacked = path.join(temporary, 'unpacked') const templateRoot = options.templateRoot @@ -104,6 +105,7 @@ export async function createSite( }) template = `${project.name}@${project.version}` packageManager = project.packageManager + hasFormatScript = project.hasFormatScript } finally { await rm(temporary, { recursive: true, force: true }) } @@ -141,6 +143,23 @@ export async function createSite( ) } } + if (result.installed && hasFormatScript) { + try { + await perform('Format project files', () => + runPackageManager(packageManager, ['run', 'format'], directory, { + capture: true, + signal: options.signal, + }), + ) + } catch (error) { + if (error instanceof Error && error.name === 'AbortError') throw error + const command = + packageManager === 'yarn' ? 'yarn format' : 'npm run format' + result.failures.push( + `Project formatting failed: ${error instanceof Error ? error.message : String(error)}\nRetry ${command} in the project directory.`, + ) + } + } if (plan.git) { try { await perform('Set up Git', async () => { diff --git a/packages/purestack/src/init/init.test.ts b/packages/purestack/src/init/init.test.ts index b99d1968..e510e4ca 100644 --- a/packages/purestack/src/init/init.test.ts +++ b/packages/purestack/src/init/init.test.ts @@ -562,11 +562,122 @@ describe('project generation', () => { expect(result.failures).toEqual([ expect.stringContaining('Retry yarn install'), ]) + expect(install).toHaveBeenCalledTimes(1) expect( await readFile(path.join(destination, 'README.md'), 'utf8'), ).toContain('PureStack') }) + it.each(['yarn', 'npm'] as const)( + 'formats after installing with %s and before setting up Git', + async (manager) => { + const templateRoot = await fixture() + if (manager === 'npm') { + const file = path.join(templateRoot, 'template/package.json') + const project = JSON.parse(await readFile(file, 'utf8')) + delete project.packageManager + await writeFile(file, JSON.stringify(project)) + } + const destination = path.join(temporary, 'project') + const run = vi.spyOn(managers, 'runPackageManager').mockResolvedValue('') + vi.spyOn(processes, 'runProcess').mockResolvedValue(repository) + const labels: string[] = [] + const signal = new AbortController().signal + const result = await createSite( + plan(destination, 'starter', { install: true, git: true }), + cliVersion, + repository, + async (label, action) => { + labels.push(label) + return action() + }, + { templateRoot, signal }, + ) + expect(result.installed).toBe(true) + expect(result.failures).toEqual([]) + expect(run.mock.calls.map((call) => call[1])).toEqual([ + manager === 'yarn' ? ['install', '--no-immutable'] : ['install'], + ['run', 'format'], + ]) + expect(run).toHaveBeenLastCalledWith( + manager, + ['run', 'format'], + destination, + { capture: true, signal }, + ) + expect(labels.slice(-3)).toEqual([ + 'Install dependencies', + 'Format project files', + 'Set up Git', + ]) + }, + ) + + it.each([undefined, ' '])( + 'skips formatting when the template has no usable format script (%s)', + async (format) => { + const templateRoot = await fixture() + const file = path.join(templateRoot, 'template/package.json') + const project = JSON.parse(await readFile(file, 'utf8')) + project.scripts.format = format + await writeFile(file, JSON.stringify(project)) + const run = vi.spyOn(managers, 'runPackageManager').mockResolvedValue('') + const result = await createSite( + plan(path.join(temporary, 'project'), 'starter', { install: true }), + cliVersion, + repository, + undefined, + { templateRoot }, + ) + expect(result.installed).toBe(true) + expect(result.failures).toEqual([]) + expect(run).toHaveBeenCalledTimes(1) + }, + ) + + it('preserves the installed project and continues Git setup when formatting fails', async () => { + const destination = path.join(temporary, 'project') + vi.spyOn(managers, 'runPackageManager') + .mockResolvedValueOnce('') + .mockRejectedValueOnce(new Error('Invalid markup')) + vi.spyOn(processes, 'runProcess').mockResolvedValue(repository) + const result = await createSite( + plan(destination, 'starter', { install: true, git: true }), + cliVersion, + repository, + undefined, + { templateRoot: await fixture() }, + ) + expect(result.installed).toBe(true) + expect(result.git).toBe('existing') + expect(result.failures).toEqual([ + 'Project formatting failed: Invalid markup\nRetry yarn format in the project directory.', + ]) + expect(await readdir(destination)).toContain('README.md') + }) + + it('propagates formatting cancellation and does not start Git setup', async () => { + const cancelled = new Error('Cancelled') + cancelled.name = 'AbortError' + vi.spyOn(managers, 'runPackageManager') + .mockResolvedValueOnce('') + .mockRejectedValueOnce(cancelled) + const git = vi.spyOn(processes, 'runProcess') + await expect( + createSite( + plan(path.join(temporary, 'project'), 'starter', { + install: true, + git: true, + }), + cliVersion, + repository, + undefined, + { templateRoot: await fixture() }, + ), + ).rejects.toBe(cancelled) + expect(git).not.toHaveBeenCalled() + }) + it('skips Git initialization inside an existing repository', async () => { const archive = await archiveFixture() vi.spyOn(console, 'log').mockImplementation(() => {}) diff --git a/packages/purestack/src/init/template.ts b/packages/purestack/src/init/template.ts index 5b707572..da7ca0bf 100644 --- a/packages/purestack/src/init/template.ts +++ b/packages/purestack/src/init/template.ts @@ -357,6 +357,8 @@ export async function prepareProject( name: pkg.name, version: pkg.version, packageManager, + hasFormatScript: + typeof scripts.format === 'string' && scripts.format.trim().length > 0, } } diff --git a/packages/ts-common/package.json b/packages/ts-common/package.json index eaa49275..ebbcec13 100644 --- a/packages/ts-common/package.json +++ b/packages/ts-common/package.json @@ -1,6 +1,6 @@ { "name": "@purestack/ts-common", - "version": "1.1.23", + "version": "1.1.24", "packageManager": "yarn@4.9.2", "module": "./dist/ts-common.mjs", "types": "./dist/ts-common.d.mts", diff --git a/packages/ts-components/package.json b/packages/ts-components/package.json index 4106687f..b38aedda 100644 --- a/packages/ts-components/package.json +++ b/packages/ts-components/package.json @@ -1,6 +1,6 @@ { "name": "@purestack/ts-components", - "version": "1.1.23", + "version": "1.1.24", "packageManager": "yarn@4.9.2", "module": "./dist/ts-components.mjs", "types": "./dist/ts-components.d.mts", diff --git a/packages/ts-components/src/standard/alertBox/alertBox.ts b/packages/ts-components/src/standard/alertBox/alertBox.ts index 96af24ef..06e430f9 100644 --- a/packages/ts-components/src/standard/alertBox/alertBox.ts +++ b/packages/ts-components/src/standard/alertBox/alertBox.ts @@ -31,7 +31,8 @@ const alertBoxTemplate = html` {{ eyebrow }} {{ title }} - {{ badge }} + {{ badge }}
diff --git a/packages/ts-components/src/standard/composer/composer.ts b/packages/ts-components/src/standard/composer/composer.ts index e8ee8220..d035332e 100644 --- a/packages/ts-components/src/standard/composer/composer.ts +++ b/packages/ts-components/src/standard/composer/composer.ts @@ -66,8 +66,7 @@ export interface Composer { const composerTemplate = html`
{{ label }} + >{{ label }}
-
+
- - - - {{ language }} - - - - - - - {{ resultTitle }} - {{ resultMeta }} - - - + + + + {{ language }} - ` + + + + + + {{ resultTitle }} + {{ resultMeta }} + + + + +` return { codeShowcase: defineComponent(codeShowcaseTemplate, { diff --git a/packages/ts-components/src/standard/landing/comparisonColumn.ts b/packages/ts-components/src/standard/landing/comparisonColumn.ts index 5e117f0a..9c6ddb53 100644 --- a/packages/ts-components/src/standard/landing/comparisonColumn.ts +++ b/packages/ts-components/src/standard/landing/comparisonColumn.ts @@ -3,21 +3,21 @@ import type { ComparisonColumn } from './landingTypes' export function defineComparisonColumnComponent() { const comparisonColumnTemplate = html` - - - - {{ badge }} - -

{{ title }}

-

{{ summary }}

- + + + + {{ badge }} -
` +

{{ title }}

+

{{ summary }}

+ +
+` return { comparisonColumn: defineComponent( diff --git a/packages/ts-components/src/standard/landing/comparisonFeature.ts b/packages/ts-components/src/standard/landing/comparisonFeature.ts index 582fae80..e2cad651 100644 --- a/packages/ts-components/src/standard/landing/comparisonFeature.ts +++ b/packages/ts-components/src/standard/landing/comparisonFeature.ts @@ -3,13 +3,13 @@ import type { ComparisonFeature } from './landingTypes' export function defineComparisonFeatureComponent() { const comparisonFeatureTemplate = html` - - - ` + + +` return { comparisonFeature: defineComponent( diff --git a/packages/ts-components/src/standard/landing/comparisonTable.ts b/packages/ts-components/src/standard/landing/comparisonTable.ts index b4ad1918..0879b0fb 100644 --- a/packages/ts-components/src/standard/landing/comparisonTable.ts +++ b/packages/ts-components/src/standard/landing/comparisonTable.ts @@ -3,24 +3,24 @@ import type { ComparisonTable } from './landingTypes' export function defineComparisonTableComponent() { const comparisonTableTemplate = html` - - - - - - - ` + + + + + + +` return { comparisonTable: defineComponent(comparisonTableTemplate, { diff --git a/packages/ts-components/src/standard/landing/ctaSection.ts b/packages/ts-components/src/standard/landing/ctaSection.ts index 3e7edd5c..ec281628 100644 --- a/packages/ts-components/src/standard/landing/ctaSection.ts +++ b/packages/ts-components/src/standard/landing/ctaSection.ts @@ -3,40 +3,40 @@ import type { CtaSection } from './landingTypes' export function defineCtaSectionComponent() { const ctaSectionTemplate = html` - - - - - {{ primaryLabel }} - - - {{ secondaryLabel }} - - - -

{{ meta }}

-
-
` + + + + + {{ primaryLabel }} + + + {{ secondaryLabel }} + + + +

{{ meta }}

+
+` return { ctaSection: defineComponent(ctaSectionTemplate, { diff --git a/packages/ts-components/src/standard/landing/featureCard.ts b/packages/ts-components/src/standard/landing/featureCard.ts index 7809980f..497d7bc3 100644 --- a/packages/ts-components/src/standard/landing/featureCard.ts +++ b/packages/ts-components/src/standard/landing/featureCard.ts @@ -3,27 +3,27 @@ import type { FeatureCard } from './landingTypes' export function defineFeatureCardComponent() { const featureCardTemplate = html` - - - - {{ badge }} - - -

{{ eyebrow }}

-

{{ title }}

-

{{ summary }}

- -
- - - + + + + {{ badge }} -
` + +

{{ eyebrow }}

+

{{ title }}

+

{{ summary }}

+ +
+ + + + +` return { featureCard: defineComponent(featureCardTemplate, { diff --git a/packages/ts-components/src/standard/landing/landingBand.ts b/packages/ts-components/src/standard/landing/landingBand.ts index 378d6d80..67f11abe 100644 --- a/packages/ts-components/src/standard/landing/landingBand.ts +++ b/packages/ts-components/src/standard/landing/landingBand.ts @@ -17,8 +17,8 @@ const LANDING_BAND_INLINE_PADDING = '1em' export function defineLandingBandComponent() { const landingBandTemplate = html`
- -
` + +
` return { landingBand: defineComponent(landingBandTemplate, { diff --git a/packages/ts-components/src/standard/landing/landingSection.ts b/packages/ts-components/src/standard/landing/landingSection.ts index e3f31522..107ea2fb 100644 --- a/packages/ts-components/src/standard/landing/landingSection.ts +++ b/packages/ts-components/src/standard/landing/landingSection.ts @@ -3,26 +3,26 @@ import type { LandingSection } from './landingTypes' export function defineLandingSectionComponent() { const landingSectionTemplate = html` + - - - - - ` + + + +
` return { landingSection: defineComponent(landingSectionTemplate, { diff --git a/packages/ts-components/src/standard/landing/metricItem.ts b/packages/ts-components/src/standard/landing/metricItem.ts index 604b7bc3..96ea2625 100644 --- a/packages/ts-components/src/standard/landing/metricItem.ts +++ b/packages/ts-components/src/standard/landing/metricItem.ts @@ -3,19 +3,21 @@ import type { MetricItem } from './landingTypes' export function defineMetricItemComponent() { const metricItemTemplate = html` - -
-

{{ label }}

- {{ value }} -

{{ detail }}

- -
-
` + +
+

{{ label }}

+ {{ value }} +

+ {{ detail }} +

+ +
+` return { metricItem: defineComponent(metricItemTemplate, { diff --git a/packages/ts-components/src/standard/landing/metricStrip.ts b/packages/ts-components/src/standard/landing/metricStrip.ts index 33a54547..ae29fb6d 100644 --- a/packages/ts-components/src/standard/landing/metricStrip.ts +++ b/packages/ts-components/src/standard/landing/metricStrip.ts @@ -3,14 +3,14 @@ import type { MetricStrip } from './landingTypes' export function defineMetricStripComponent() { const metricStripTemplate = html` - - - - ` + + + +` return { metricStrip: defineComponent(metricStripTemplate, { diff --git a/packages/ts-components/src/standard/logo/logo.ts b/packages/ts-components/src/standard/logo/logo.ts index 88f7b096..2eeeac30 100644 --- a/packages/ts-components/src/standard/logo/logo.ts +++ b/packages/ts-components/src/standard/logo/logo.ts @@ -71,14 +71,10 @@ const logoTemplate = html`
` diff --git a/packages/ts-components/src/standard/modal/modalStore.ts b/packages/ts-components/src/standard/modal/modalStore.ts index d163b6eb..a96a03ec 100644 --- a/packages/ts-components/src/standard/modal/modalStore.ts +++ b/packages/ts-components/src/standard/modal/modalStore.ts @@ -126,8 +126,12 @@ const modalStoreTemplate = html`