feature-grid, feature-split, pricing, testimonials, faq, article-grid, team y contact declaraban cada uno su `.Header` con las mismas tres líneas: un Box con maxWidth 48rem, un Motion trigger="viewport" y un Stack gap 3. Ocho ficheros, una sola idea. Y la copia se pagó donde siempre: article-grid había perdido su Motion. Su encabezado aparecía sin el reveal que tenían los siete hermanos, y nadie lo notó porque no hay dos secciones iguales delante a la vez. Componer el canon lo restaura — medido: su primer hijo estampa ya `data-animation-trigger`, igual que el de pricing. Los ocho pasan a componer `Section.Header`, que gana `align` para la única variación real: feature-grid hace llegar el eje de la sección al contenido del encabezado por contexto (A-84) y article-grid alinea a start. faq conserva su `marginX="auto"` en el call site. Ninguna parte pública desaparece: `Pricing.Header` sigue siendo `Pricing.Header`, sólo que ahora hay una implementación en vez de ocho. Medido en las ocho previews: 768px de medida (48rem) donde tocaba, faq en sus 736 por su propio centrado, y el alignItems de cada Stack en lo que su block pide — center en feature-grid, start en article-grid, stretch en el resto. Gates: svelte-check 72/62 antes y después · vitest src/uix/blocks 36/36 · blocks:check 0/19. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>alpha-0.1-background
parent
68eb8af260
commit
ecb23d6a48
@ -1,15 +1,17 @@
|
||||
<script lang="ts">
|
||||
/** ArticleGrid.Header — the section's own heading and lead. The app owns the
|
||||
* words and the level; the block only caps the measure. */
|
||||
import { Box } from '$uix/eidos/components/box';
|
||||
import { Stack } from '$uix/eidos/components/stack';
|
||||
/**
|
||||
* ArticleGrid.Header — the section's name and its supporting line. It composes the
|
||||
* canon `Section.Header` (2026-08-19): eight blocks of the tier shipped the
|
||||
* same three lines, and one copy had already drifted. ⚠️ This one WAS the drifted copy: it had lost the `Motion`, so its heading
|
||||
* appeared without the reveal every sibling section had. Composing the canon
|
||||
* restores it.
|
||||
*/
|
||||
import { Section } from '$uix/eidos/components/section';
|
||||
import type { ArticleGridHeaderProps } from './types';
|
||||
|
||||
let { maxWidth = '48rem', children, ...rest }: ArticleGridHeaderProps = $props();
|
||||
let { children, ...rest }: ArticleGridHeaderProps = $props();
|
||||
</script>
|
||||
|
||||
<Box {maxWidth} {...rest}>
|
||||
<Stack gap={3} align="start">
|
||||
{@render children?.()}
|
||||
</Stack>
|
||||
</Box>
|
||||
<Section.Header align="start" {...rest}>
|
||||
{@render children?.()}
|
||||
</Section.Header>
|
||||
|
||||
@ -1,20 +1,15 @@
|
||||
<script lang="ts">
|
||||
/**
|
||||
* Contact.Header — the section title + description, measure-capped. The app
|
||||
* supplies a `Heading` (level 2) and a `Text`; their alignment is the app's.
|
||||
* Contact.Header — the section's name and its supporting line. It composes the
|
||||
* canon `Section.Header` (2026-08-19): eight blocks of the tier shipped the
|
||||
* same three lines, and one copy had already drifted.
|
||||
*/
|
||||
import { Box } from '$uix/eidos/components/box';
|
||||
import { Stack } from '$uix/eidos/components/stack';
|
||||
import { Motion } from '$uix/eidos/components/motion';
|
||||
import { Section } from '$uix/eidos/components/section';
|
||||
import type { ContactHeaderProps } from './types';
|
||||
|
||||
let { maxWidth = '48rem', children, ...rest }: ContactHeaderProps = $props();
|
||||
let { children, ...rest }: ContactHeaderProps = $props();
|
||||
</script>
|
||||
|
||||
<Box {maxWidth} width="100%" {...rest}>
|
||||
<Motion trigger="viewport">
|
||||
<Stack gap={3}>
|
||||
{@render children?.()}
|
||||
</Stack>
|
||||
</Motion>
|
||||
</Box>
|
||||
<Section.Header {...rest}>
|
||||
{@render children?.()}
|
||||
</Section.Header>
|
||||
|
||||
@ -1,28 +1,15 @@
|
||||
<script lang="ts">
|
||||
/** Faq.Header — the section title + description. The app supplies a `Heading`
|
||||
* (level 2) and a `Text`. */
|
||||
import { Box } from '$uix/eidos/components/box';
|
||||
import { Stack } from '$uix/eidos/components/stack';
|
||||
import { Motion } from '$uix/eidos/components/motion';
|
||||
/**
|
||||
* Faq.Header — the section's name and its supporting line. It composes the
|
||||
* canon `Section.Header` (2026-08-19): eight blocks of the tier shipped the
|
||||
* same three lines, and one copy had already drifted. The block centres it in its measure.
|
||||
*/
|
||||
import { Section } from '$uix/eidos/components/section';
|
||||
import type { FaqHeaderProps } from './types';
|
||||
|
||||
let { maxWidth = '48rem', children, ...rest }: FaqHeaderProps = $props();
|
||||
let { children, ...rest }: FaqHeaderProps = $props();
|
||||
</script>
|
||||
|
||||
<!--
|
||||
`marginX="auto"` is what makes the typedoc's «centered» true (A-19). `Box` does
|
||||
not centre on its own — `maxWidth` rides `--box-max-width` and the margin is a
|
||||
separate channel — and the root stacks with `align="stretch"`, so once the cap
|
||||
bites the header anchored at cross-start instead. Same idiom the sibling block
|
||||
already uses (`content-section-media.svelte`). With this block's own defaults
|
||||
the cap never bites (a `md` container is narrower than `48rem`), so nothing
|
||||
moves until a consumer widens the section — which is exactly the case the
|
||||
promise was written for.
|
||||
-->
|
||||
<Box {maxWidth} width="100%" marginX="auto" {...rest}>
|
||||
<Motion trigger="viewport">
|
||||
<Stack gap={3}>
|
||||
{@render children?.()}
|
||||
</Stack>
|
||||
</Motion>
|
||||
</Box>
|
||||
<Section.Header marginX="auto" {...rest}>
|
||||
{@render children?.()}
|
||||
</Section.Header>
|
||||
|
||||
@ -1,30 +1,21 @@
|
||||
<script lang="ts">
|
||||
/**
|
||||
* FeatureGrid.Header — the section title + description, measure-capped and
|
||||
* centered by the parent. The app supplies a `Heading` (level 2) and a `Text`;
|
||||
* their alignment is the app's to choose.
|
||||
* FeatureGrid.Header — the section's name and its supporting line. It composes
|
||||
* the canon `Section.Header` (2026-08-19) and adds the one thing that is this
|
||||
* block's: the section's `align` axis reaches the header's own content through
|
||||
* context (A-84), so it is set once on the root instead of being repeated on
|
||||
* the `Heading` and the `Text` the app drops in here.
|
||||
*/
|
||||
import { Box } from '$uix/eidos/components/box';
|
||||
import { Stack } from '$uix/eidos/components/stack';
|
||||
import { Motion } from '$uix/eidos/components/motion';
|
||||
import { Section } from '$uix/eidos/components/section';
|
||||
import { getFeatureGridContext } from './context';
|
||||
import type { FeatureGridHeaderProps } from './types';
|
||||
|
||||
let { align, maxWidth = '48rem', children, ...rest }: FeatureGridHeaderProps = $props();
|
||||
let { align, children, ...rest }: FeatureGridHeaderProps = $props();
|
||||
|
||||
const grid = getFeatureGridContext();
|
||||
const resolvedAlign = $derived(align ?? grid?.align ?? 'center');
|
||||
</script>
|
||||
|
||||
<!--
|
||||
The section's axis reaches the header's own content (A-84): it used to align
|
||||
only the header BOX, and the type told the app to repeat the alignment on the
|
||||
`Heading` and the `Text` it drops in here.
|
||||
-->
|
||||
<Box {maxWidth} width="100%" {...rest}>
|
||||
<Motion trigger="viewport">
|
||||
<Stack gap={3} align={resolvedAlign}>
|
||||
{@render children?.()}
|
||||
</Stack>
|
||||
</Motion>
|
||||
</Box>
|
||||
<Section.Header align={resolvedAlign} {...rest}>
|
||||
{@render children?.()}
|
||||
</Section.Header>
|
||||
|
||||
@ -1,26 +1,15 @@
|
||||
<script lang="ts">
|
||||
/**
|
||||
* FeatureSplit.Header — the section title + description, measure-capped. The
|
||||
* app supplies a `Heading` (level 2) and a `Text`; their alignment is the
|
||||
* app's to choose, as in `Testimonials.Header` (this block has no `align`
|
||||
* axis of its own, so there is nothing to inherit).
|
||||
*
|
||||
* It exists because a section names itself through its header (A-110): this
|
||||
* was the only section block of the tier without one, so a three-row section
|
||||
* contributed three sibling `h2`s and no name of its own.
|
||||
* FeatureSplit.Header — the section's name and its supporting line. It composes the
|
||||
* canon `Section.Header` (2026-08-19): eight blocks of the tier shipped the
|
||||
* same three lines, and one copy had already drifted.
|
||||
*/
|
||||
import { Box } from '$uix/eidos/components/box';
|
||||
import { Stack } from '$uix/eidos/components/stack';
|
||||
import { Motion } from '$uix/eidos/components/motion';
|
||||
import { Section } from '$uix/eidos/components/section';
|
||||
import type { FeatureSplitHeaderProps } from './types';
|
||||
|
||||
let { maxWidth = '48rem', children, ...rest }: FeatureSplitHeaderProps = $props();
|
||||
let { children, ...rest }: FeatureSplitHeaderProps = $props();
|
||||
</script>
|
||||
|
||||
<Box {maxWidth} width="100%" {...rest}>
|
||||
<Motion trigger="viewport">
|
||||
<Stack gap={3}>
|
||||
{@render children?.()}
|
||||
</Stack>
|
||||
</Motion>
|
||||
</Box>
|
||||
<Section.Header {...rest}>
|
||||
{@render children?.()}
|
||||
</Section.Header>
|
||||
|
||||
@ -1,18 +1,15 @@
|
||||
<script lang="ts">
|
||||
/** Pricing.Header — the section title + description, measure-capped and centered
|
||||
* by the parent. The app supplies a `Heading` (level 2) and a `Text`. */
|
||||
import { Box } from '$uix/eidos/components/box';
|
||||
import { Stack } from '$uix/eidos/components/stack';
|
||||
import { Motion } from '$uix/eidos/components/motion';
|
||||
/**
|
||||
* Pricing.Header — the section's name and its supporting line. It composes the
|
||||
* canon `Section.Header` (2026-08-19): eight blocks of the tier shipped the
|
||||
* same three lines, and one copy had already drifted.
|
||||
*/
|
||||
import { Section } from '$uix/eidos/components/section';
|
||||
import type { PricingHeaderProps } from './types';
|
||||
|
||||
let { maxWidth = '48rem', children, ...rest }: PricingHeaderProps = $props();
|
||||
let { children, ...rest }: PricingHeaderProps = $props();
|
||||
</script>
|
||||
|
||||
<Box {maxWidth} width="100%" {...rest}>
|
||||
<Motion trigger="viewport">
|
||||
<Stack gap={3}>
|
||||
{@render children?.()}
|
||||
</Stack>
|
||||
</Motion>
|
||||
</Box>
|
||||
<Section.Header {...rest}>
|
||||
{@render children?.()}
|
||||
</Section.Header>
|
||||
|
||||
@ -1,21 +1,15 @@
|
||||
<script lang="ts">
|
||||
/**
|
||||
* Team.Header — the section title + description, measure-capped and placed by
|
||||
* the parent. The app supplies a `Heading` (level 2) and a `Text`; their own
|
||||
* alignment is the app's to choose.
|
||||
* Team.Header — the section's name and its supporting line. It composes the
|
||||
* canon `Section.Header` (2026-08-19): eight blocks of the tier shipped the
|
||||
* same three lines, and one copy had already drifted.
|
||||
*/
|
||||
import { Box } from '$uix/eidos/components/box';
|
||||
import { Stack } from '$uix/eidos/components/stack';
|
||||
import { Motion } from '$uix/eidos/components/motion';
|
||||
import { Section } from '$uix/eidos/components/section';
|
||||
import type { TeamHeaderProps } from './types';
|
||||
|
||||
let { maxWidth = '48rem', children, ...rest }: TeamHeaderProps = $props();
|
||||
let { children, ...rest }: TeamHeaderProps = $props();
|
||||
</script>
|
||||
|
||||
<Box {maxWidth} width="100%" {...rest}>
|
||||
<Motion trigger="viewport">
|
||||
<Stack gap={3}>
|
||||
{@render children?.()}
|
||||
</Stack>
|
||||
</Motion>
|
||||
</Box>
|
||||
<Section.Header {...rest}>
|
||||
{@render children?.()}
|
||||
</Section.Header>
|
||||
|
||||
@ -1,18 +1,15 @@
|
||||
<script lang="ts">
|
||||
/** Testimonials.Header — the section title + description, measure-capped and
|
||||
* centered by the parent. The app supplies a `Heading` (level 2) and a `Text`. */
|
||||
import { Box } from '$uix/eidos/components/box';
|
||||
import { Stack } from '$uix/eidos/components/stack';
|
||||
import { Motion } from '$uix/eidos/components/motion';
|
||||
/**
|
||||
* Testimonials.Header — the section's name and its supporting line. It composes the
|
||||
* canon `Section.Header` (2026-08-19): eight blocks of the tier shipped the
|
||||
* same three lines, and one copy had already drifted.
|
||||
*/
|
||||
import { Section } from '$uix/eidos/components/section';
|
||||
import type { TestimonialsHeaderProps } from './types';
|
||||
|
||||
let { maxWidth = '48rem', children, ...rest }: TestimonialsHeaderProps = $props();
|
||||
let { children, ...rest }: TestimonialsHeaderProps = $props();
|
||||
</script>
|
||||
|
||||
<Box {maxWidth} width="100%" {...rest}>
|
||||
<Motion trigger="viewport">
|
||||
<Stack gap={3}>
|
||||
{@render children?.()}
|
||||
</Stack>
|
||||
</Motion>
|
||||
</Box>
|
||||
<Section.Header {...rest}>
|
||||
{@render children?.()}
|
||||
</Section.Header>
|
||||
|
||||
Loading…
Reference in new issue