// A titled block inside a page. Exists so a screen's second-level structure is a labelled
// landmark with a real heading — `aria-labelledby` wired to the heading it renders — instead of
// a
with bold text, which is what an unassisted layout always becomes.
import type { JSX } from 'solid-js';
import { useId } from '../a11y';
import { cx } from '../cx';
import { type HeadingLevel, headingTag } from './heading-level';
import { headingNode } from './heading-node';
import styles from './Section.module.scss';
export interface SectionProps {
children: JSX.Element;
/** Already-translated section title. Omit for an unlabelled grouping. */
title?: string | undefined;
/** Already-translated supporting line under the title. */
description?: string | undefined;
/** Controls that act on this section only. */
actions?: JSX.Element | undefined;
/** Heading level. 2 by default — the level under a PageHeader's h1. */
level?: HeadingLevel | undefined;
as?: 'section' | 'article' | 'aside' | undefined;
class?: string | undefined;
}
export function Section(props: SectionProps): JSX.Element {
// Resolved once, so an off-scale level throws `X_UI_INVALID_VALUE` here as it always did.
const heading = headingTag(props.level ?? 2);
const titleId = useId('section');
const cls = (): string => cx(styles['section'], props.class);
const labelledBy = (): string | undefined => (props.title === undefined ? undefined : titleId);
const title = (): string | undefined => props.title;
const head = (): JSX.Element =>
props.title === undefined && props.actions === undefined ? null : (
;
// One intrinsic per case, never `const Tag = props.as; `: the island build compiles a
// capitalised tag to `createComponent(Tag)` and calls the string (#488, `intrinsic-root.test.ts`).
switch (props.as ?? 'section') {
case 'article':
return (
{head()}
{body()}
);
case 'aside':
return (
);
case 'section':
return (
{head()}
{body()}
);
}
}