# Link Card

Canonical: https://socra.design/landing/link-card

Destinations in a directory: a mark, a name and one sentence, each card one link.

List the apps, services or integrations a page leads to. The page passes each mark, name, sentence and destination; the card owns the 40px mark, the item-title name, the secondary sentence and the shared Item interaction, and the grid owns the columns and a centered last row.

Status: ready. Shared landing link cards (landing.LinkCard on the Web UI Item — a 40px mark sized by the card whatever size it was given, an h3 name on the item-title role, a secondary body sentence, the whole card one link named by its title and described by its sentence; landing.LinkCardGrid — one, two and three columns at the phone, sm and md steps, equal heights per row and a centered short last row) lives in @socra/ui-web-landing.

Library: @socra/ui-web-landing

## Anatomy

- Intent, states, feedback, hierarchy, and access requirements are independent of any renderer.
- Every shared visual relationship resolves from semantic design roles without changing their meaning between platforms.
- Each platform uses its native mechanics and provides live conformance evidence without changing the guideline.

## States

- **Rest:** Shows the stable, enabled component without implied activity.
- **Hover:** Every enabled clickable target shows a pointing hand. Keep the shared visual hover treatment independent, without moving content or changing meaning.
- **Focus:** Shows the shared visible focus treatment without depending on hover.
- **Pressed:** Acknowledges active input immediately and returns cleanly on release.
- **Selected:** Uses a neutral surface plus native selected semantics when selection applies.
- **Disabled:** Remains legible, unavailable, and absent from misleading interaction feedback.
- **Loading:** Preserves context while honestly identifying work that has not completed.
- **Error:** Places the failure and recovery path beside the action or content that failed.

## Motion

Stable state changes respond immediately. Appearance, expansion, and morphing begin at the triggering origin and use an interruptible spring that can retarget from its live position and velocity. When spatial motion is not meaningful, the component changes without decorative travel.

## Usage

- Use the owning platform implementation first. Each renderer improves once and every product on that platform inherits the fix.
- Name the user job before the component. A component earns a place only when it helps the user decide, act, recover, or understand.
- Promote missing behavior into the owning platform domain. Product-local UI creates drift; platform contracts preserve shared meaning and native quality.
- Raise luminance to bring a surface forward. Depth reads as light. A surface that comes toward the reader catches more of it, so a raised panel, a selected tab, a menu, a popover resolves LIGHTER than what it sits on — in dark mode away from the canvas, in light mode toward the white it cannot exceed. The ladder runs page, resting card, elevated, and never doubles back.

## Avoid

- Do not build a product-local replacement. A local implementation splits behavior, access requirements, and future migrations.
- Do not copy theme values. Semantic values are shared system roles and are consumed by meaning.
- Do not add decoration as a substitute for structure. If the hierarchy is weak, fix the hierarchy.
- Do not darken a surface to lift it. Darkening in a dark theme pushes a surface INTO the page — it reads as a hole cut in the canvas rather than a panel resting on it. Sinking a surface to make it stand out is the most common way a dark interface loses its depth.

## Tokens

- `designTokens.color[mode].color.brand.primary`: Primary action role.
- `designTokens.color[mode].color.surface.secondary`: Working surface role.
- `designTokens.color[mode].color.content.primary`: Primary content role.
- `designTokens.spacing.role.contentGap`: Spacing relationship.
- `designTokens.shape.role.control`: Shape relationship.

## Properties

[Complete public API, including inherited properties and event signatures](https://socra.design/landing/link-card/api.md)

### Setup

- Use the host-selected compatible Socra theme provider and published component styles. Keep one compatible provider instance across shared packages.

### LinkCard

Import: `import { LinkCard } from '@socra/ui-web-landing';`

One destination's decorative mark, name and sentence rendered as one native link.

| Property | Type or permitted values | Default / omission | Required | Meaning and behavior | Inspector |
| --- | --- | --- | --- | --- | --- |
| `ariaLabel` | `string \| undefined` | The visible title names the link via aria-labelledby. An empty string also retains the title reference. | no | An optional more specific accessible name for this link. When supplied, use a nonempty name that preserves the visible title's meaning. A truthy value replaces the title naming reference with aria-label; description association remains. | Direct API |
| `description` | `ReactNode` | Required input | yes | One sentence explaining the destination, rendered in secondary Body text. Provide noninteractive content appropriate inside a link; the whole card owns activation. The sentence describes the link through aria-describedby, including when ariaLabel overrides its name. | Direct API |
| `href` | `string` | Required input | yes | The destination of the card's native anchor. Provide a meaningful supported destination. The component does not implement disabled links, host navigation callbacks, target or other arbitrary anchor attributes. The browser owns activation, modifier clicks and navigation. Hosts omit unavailable destinations or use the appropriate non-navigation composition. | Direct API |
| `mark` | `ReactNode` | Required input | yes | Decorative identity or illustration for the destination; the title supplies its name. Supply noninteractive artwork. The card reserves the large navigation mark box and sizes a direct image or SVG to that box; custom wrapped artwork must fit the same box. The mark slot is hidden from assistive technology. Card-owned geometry scales from the shared body text size (40 CSS pixels at the default size); consumers do not select a mark size. | Direct API |
| `title` | `ReactNode` | Required input | yes | The visible destination name, rendered as an h3 using the item-title role. Provide meaningful noninteractive content. Keep the name concise; title content is also the default accessible link name. | Direct API |

### LinkCardGrid

Import: `import { LinkCardGrid } from '@socra/ui-web-landing';`

A named list of destination cards; the shared grid owns responsive columns and row alignment.

| Property | Type or permitted values | Default / omission | Required | Meaning and behavior | Inspector |
| --- | --- | --- | --- | --- | --- |
| `items` | `readonly LinkCardGridItem[]` | Required input | yes | Destination entries in reading order. Each LinkCardGridItem contains id, mark, title, description, href and optional ariaLabel. Use unique stable string ids, including when reordering. Each entry obeys LinkCard's content and link constraints. Every item renders as one list item containing one LinkCard. An empty array renders an empty named list; the host owns any empty-state explanation. The grid derives one, two and three columns at the phone, 600px and 1180px steps, equal row heights and a centered short last row. | Direct API |
| `label` | `string` | Required input | yes | The accessible name of the destination list. Required: provide a meaningful nonempty list name. LinkCardGrid has no declared label default. Applied to the ul as aria-label. The catalog's Destinations fallback belongs to its specimen adapter. | Direct API |

### Band

Import: `import { Band } from '@socra/ui-web-landing';`

| Property | Type or permitted values | Default / omission | Required | Meaning and behavior | Inspector |
| --- | --- | --- | --- | --- | --- |
| `ariaLabelledby` | `string \| undefined` | Omitted; no declared default | no | id of the band's heading — keeps the <section> named for screen readers. | Direct API |
| `bleed` | `false`, `true` | `false` | no | Band owns its full width (carousels): children render edge-to-edge with no content column. The child then re-creates the column where it needs one. | Direct API |
| `children` | `ReactNode` | Required input | yes |  | Direct API |
| `colorMode` | `"dark"`, `"ambient"` | `"ambient"` | no | whether the band is fixed dark or follows the ambient page theme. | Direct API |
| `contentMaxWidth` | `string \| undefined` | `"1120px"` | no | content column width; the production landing column. | Direct API |
| `divider` | `false`, `true` | `false` | no | The divider at a standalone band's leading edge. Inside a marketing page the band sequence puts the divider on every boundary, so pages do not set it. | Direct API |
| `id` | `string \| undefined` | Omitted; no declared default | no | anchor id for in-page navigation. | Direct API |
| `spacing` | `"none"`, `"compact"`, `"screen"`, `"regular"` | `"regular"` | no | the band's vertical rhythm. | Direct API |
| `tone` | `"base"`, `"surface"`, `"raised"` | `"base"` | no | A standalone band's surface step. Inside a marketing page the band sequence alternates base and surface by position, so pages do not set it. | Direct API |

### BandIntro

Import: `import { BandIntro } from '@socra/ui-web-landing';`

| Property | Type or permitted values | Default / omission | Required | Meaning and behavior | Inspector |
| --- | --- | --- | --- | --- | --- |
| `align` | `"center"`, `"responsive"` | `"responsive"` | no | whether the column centers at every width or only from \`md\`. | Direct API |
| `children` | `ReactNode` | Omitted; no declared default | no | extra intro content below the description (a second paragraph, a CTA). | Direct API |
| `description` | `string \| undefined` | Omitted; no declared default | no | optional standfirst under the heading (\`subhead\` role). | Direct API |
| `headingScale` | `"section"`, `"statement"` | `'section'` | no | the heading scale. | Direct API |
| `headingTreatment` | `"solid"`, `"gradient"` | `'gradient'` | no | the heading paint treatment. | Direct API |
| `id` | `string \| undefined` | Omitted; no declared default | no | heading id — the target of the owning band's \`ariaLabelledby\`. | Direct API |
| `level` | `"h1"`, `"h2"` | `'h2'` | no | the heading level; the page's first band passes 'h1'. | Direct API |
| `title` | `ReactNode` | Required input | yes | the band heading, rendered through <BandHeader/>. | Direct API |

### Behavior and constraints

- LinkCardProps, LinkCardGridProps and LinkCardGridItem are public type exports. LinkCardGridItem adds a stable string id to all LinkCardProps; the id is used as the React key, not as a DOM anchor id.
- The catalog composes LinkCardGrid inside the public Band and BandIntro. Accessible name and Cards are the two controls in upstream 82de5e737; neither creates an additional public component property.
- All four content/destination fields on LinkCard are required, with no declared defaults. ariaLabel is optional. The composition owns its fixed muted Item treatment, h3/item-title name, secondary Body sentence and decorative mark size; no independent size, tone or disabled axis is exposed.
- The catalog uses neutral fictional fragment hrefs (#product, #account and the other fixture identities) to demonstrate native link semantics. These are preview-only destinations, not configured product pages or a claim that a target exists in the specimen. Product hosts supply real supported hrefs; the shared card forwards them to its native anchor unchanged.

### Inspector mappings

| Setting | Initial preview value | Consumed code |
| --- | --- | --- |
| Accessible name | `Destinations` | composition: Passes String(configuration.label ?? 'Destinations').trim() \|\| 'Destinations' to LinkCardGrid.label, so a blank inspector value retains a named preview list. The public label itself remains required without a default. |
| Cards | `6` | composition: Converts the selected string count to a number and slices the six neutral fixture destinations to construct LinkCardGrid.items. It does not supply a count property; every retained item keeps its stable id and LinkCard content. |
