You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
120 lines
4.2 KiB
120 lines
4.2 KiB
<script lang="ts">
|
|
/**
|
|
* SiteFooter — the end of the page: the brand, the link columns, an optional
|
|
* sign-up band, and a bottom bar with the legal line, the social links and
|
|
* whatever the app wants to park there (language / theme pickers, D-BLK.6).
|
|
*
|
|
* FORM: compound for what REPEATS (`Column`, one per link group — the app maps
|
|
* over N), snippet slots for what does not (brand, signup, social, legal,
|
|
* extra). That is the tier's admission rule, applied part by part.
|
|
*
|
|
* The landmark is a real `<footer>`, so it is `contentinfo` for free. The
|
|
* column titles are `Heading level={3}` — a footer group HAS a name, and the
|
|
* references mark it up the same way.
|
|
*
|
|
* B contract: canon components only — no `.css`, no morfo, no strings of its
|
|
* own. It does NOT import the `newsletter` block for the sign-up band (B-10):
|
|
* the app composes its own form there.
|
|
*/
|
|
import { Section } from '$uix/eidos/components/section';
|
|
import { Container } from '$uix/eidos/components/container';
|
|
import { Stack } from '$uix/eidos/components/stack';
|
|
import { Flex } from '$uix/eidos/components/flex';
|
|
import { Grid } from '$uix/eidos/components/grid';
|
|
import { Box } from '$uix/eidos/components/box';
|
|
import { AutoGrid } from '$uix/eidos/components/auto-grid';
|
|
import { Separator } from '$uix/eidos/components/separator';
|
|
import { Motion } from '$uix/eidos/components/motion';
|
|
import type { SiteFooterProps } from './types';
|
|
|
|
let {
|
|
// `lg`, the same measure `hero` / `cta` / `newsletter` default to. A footer
|
|
// wider than every other section of the page reads as misaligned — verified
|
|
// by composing it under a `lg` section.
|
|
containerSize = 'lg',
|
|
sectionSize = 'lg',
|
|
minChildWidth = '7.5rem',
|
|
brand,
|
|
signup,
|
|
social,
|
|
legal,
|
|
extra,
|
|
children,
|
|
...rest
|
|
}: SiteFooterProps = $props();
|
|
</script>
|
|
|
|
<footer {...rest}>
|
|
<Section size={sectionSize}>
|
|
<Container size={containerSize}>
|
|
<!--
|
|
One entrance for the whole footer. No stagger: nobody reads a footer
|
|
column by column, and dealing five of them in would draw the eye to the
|
|
least important part of the page.
|
|
-->
|
|
<Motion trigger="viewport">
|
|
<Stack gap={8}>
|
|
<!-- Top region: the brand at the inline-start, the columns filling the
|
|
rest. Stacks on narrow, where two tracks leave the columns unusable. -->
|
|
<!--
|
|
`1fr 3fr`, not `1fr 2fr`: measured at the `lg` measure, a 2fr track
|
|
only fits three 7.5rem columns, so a four-group footer broke into
|
|
3 + 1. The brand needs a quarter; the columns need the rest.
|
|
-->
|
|
<Grid
|
|
templateColumns={{ base: '1fr', md: 'minmax(0, 1fr) minmax(0, 3fr)' }}
|
|
gap={{ base: 8, md: 10 }}
|
|
align="start"
|
|
>
|
|
{#if brand}
|
|
<Box>{@render brand()}</Box>
|
|
{/if}
|
|
<!-- Fluid by construction: as many columns as fit, no breakpoints. -->
|
|
<!-- Tighter between columns than between wrapped rows: the horizontal
|
|
gap is what decides how many groups share a row (five of them fit
|
|
at the `lg` measure with `6`, not with `8`), while the vertical one
|
|
has to separate two bands of links. -->
|
|
<AutoGrid {minChildWidth} columnGap={6} rowGap={8} align="start">
|
|
{@render children?.()}
|
|
</AutoGrid>
|
|
</Grid>
|
|
|
|
{#if signup}
|
|
<!-- Its own band: in the brand column an email row would have to stack. -->
|
|
<Box>{@render signup()}</Box>
|
|
{/if}
|
|
</Stack>
|
|
</Motion>
|
|
|
|
{#if legal || social || extra}
|
|
<!-- The bottom bar is outside the `Motion`: a copyright line that fades in
|
|
is theatre. The separator is decorative — the `<footer>` landmark
|
|
already tells AT where this region starts. -->
|
|
<Stack gap={6} marginTop={8}>
|
|
<Separator decorative />
|
|
<Flex
|
|
direction={{ base: 'column', sm: 'row' }}
|
|
justify="space-between"
|
|
align={{ base: 'start', sm: 'center' }}
|
|
gap={4}
|
|
>
|
|
{#if legal}
|
|
<Box>{@render legal()}</Box>
|
|
{/if}
|
|
{#if social || extra}
|
|
<Flex direction="row" align="center" gap={4}>
|
|
{#if social}
|
|
{@render social()}
|
|
{/if}
|
|
{#if extra}
|
|
{@render extra()}
|
|
{/if}
|
|
</Flex>
|
|
{/if}
|
|
</Flex>
|
|
</Stack>
|
|
{/if}
|
|
</Container>
|
|
</Section>
|
|
</footer>
|