Components
Initial reusable Button, Link, Text Input and Form Field candidates, built from the working foundations.
Initial reusable components
Button, Link, Text Input and Form Field are implemented reusable React components in the Release 1.0 Approval Candidate. They consume the accepted foundations independently of this documentation shell or a particular corporate website. Their existing component-level working treatments are internally accepted for this release scope. Specialist and application-specific validation and final company approval remain pending.
Only these four components are included. No icon-only action, loading state, destructive variant, custom validation engine or production form workflow is provided. Documentation specimens below use the actual components and generic illustrative text.
Button
Use a Button for an action and a Link for navigation. Anatomy: native button, wrapping text label, control boundary and an external keyboard focus ring. Primary actions use accessible interactive orange, not decorative brand orange. Secondary actions use charcoal on white with a meaningful control border.
Use Tab to see the actual keyboard focus ring. Press Space or Enter to activate a focused button. Pressed colour lasts only while pressing.
| Property | Contract |
|---|---|
| variant | "primary" (default) or "secondary". No additional visual variants. |
| type | Defaults to "button". Explicit "submit" or "reset" enables native form behaviour. |
| Native props | ButtonProps extends ComponentPropsWithRef<"button">. Native attributes, data/ARIA attributes, children, event handlers and HTMLButtonElement refs are forwarded. |
| States | Default, hover, momentary active, focus-visible and native disabled. Active is not a persistent toggle. No loading or toggle API. |
| Internally accepted working treatment | Primary boundary follows its fill. Secondary hover/active use existing secondary/tertiary surfaces. Accepted for the Release 1.0 component scope; no new global tokens. |
import { Button } from "@/components/ui/button";
<Button onClick={handleAction}>Example action</Button>
<Button variant="secondary" type="submit">Submit example</Button>
<Button disabled>Unavailable action</Button>Link
Use a real destination, not an action disguised as navigation. Anatomy: native anchor, descriptive destination text and visible underline/focus. Inline text links wrap with their paragraph. Standalone links retain the same colour and underline, using UI metrics and a minimum 48px target.
Read the Colour Foundation reference for the action palette.
Read component statusBoth examples are native destinations. Tab to a link and press Enter to navigate. Inline links retain paragraph typography; standalone links use UI metrics and a 48px minimum target.
| Property | Contract |
|---|---|
| href | Required string. Preserve real native destinations, fragments, download and target semantics. Destination validity remains the caller's responsibility. |
| context | "inline" (default) or "standalone". Context changes layout and typography, not destination semantics. |
| Native props | LinkProps forwards native anchor attributes, events and HTMLAnchorElement refs. No disabled-link API. |
| Internally accepted working treatment | Underlined interactive-orange text; hover/active reuse existing action roles. Visited links retain the same palette. Accepted for these Release 1.0 components, without establishing a new global visited-colour token. |
import { Link } from "@/components/ui/link";
<Link href="/docs/foundations/colours">Colour reference</Link>
<Link context="standalone" href="#details">Read details</Link>The base anchor has no Next.js dependency. A future application-specific router adapter can compose it through the router's supported anchor interface, forwarding href, attributes and ref once. An adapter needs its own navigation tests; none is introduced here.
Text Input
Use Text Input for a standard single-line text value. Anatomy: native text input, readable value, meaningful border and external focus ring. Always provide an associated visible label, directly or through Form Field. A placeholder is supplementary, never the label.
An empty value fails native required validation. No submission is performed here.
Disabled controls are unavailable and omitted from native form submission.
Read-only values remain focusable, selectable and eligible for native form submission.
Error: review this illustrative value. No business validation rule is applied.
This identifier tests Arabic CSS metrics and RTL alignment without providing Arabic-language copy. It is not a language-validation specimen.
| Property | Contract |
|---|---|
| Native props | TextInputProps is ComponentPropsWithRef<"input"> with type restricted to text. Forwards id, name, required, disabled, readOnly, autoComplete, value/defaultValue, onChange, language, direction, ARIA/data attributes and HTMLInputElement ref. |
| Value ownership | Controlled value/onChange or uncontrolled defaultValue. No internal state, validation rules or forced controlled mode. |
| Invalid | aria-invalid true, grammar or spelling activates a 2px error boundary; false/absent does not. Supply explicit error wording and associate it through aria-describedby. Padding compensates the border change to keep geometry stable. |
| Read-only / disabled | Native readOnly remains focusable and submitted. Native disabled is unavailable and omitted from submission. The read-only warm secondary fill is an internally accepted working cue for Release 1.0; disabled uses accepted fill/text without opacity. |
| Out of scope | Passwords, email/telephone specialisations, masks, autocomplete widgets, automatic success borders and floating labels. |
import { TextInput } from "@/components/ui/text-input";
<label htmlFor="example">Example text</label>
<TextInput id="example" name="example" defaultValue="Illustrative value" />Form Field
Use Form Field when a single text input needs a visible label and optional helper/error descriptions. Anatomy and reading order: label, exactly one Text Input, helper, error. This wrapper adds associations, not business validation, submission handling or form state.
Caller-owned description: values below are generic documentation examples.
Reset restores the uncontrolled default; the controlled value remains owned by its caller. The input ref resolves to the native input.
| Property | Contract |
|---|---|
| id / label | Both required strings. Caller owns a stable, unique, whitespace-free id. Reserve id-helper and id-error for this field's generated descriptions. The label's htmlFor points to that id; empty or whitespace-bearing IDs are rejected. |
| helperText / errorText | Optional strings. Non-empty errorText sets aria-invalid=true and must identify the error in words. No role=alert is forced on static feedback; dynamic announcement policy remains the application's responsibility. |
| aria-describedby | Caller IDs remain first; helper/error IDs are appended, deduplicated and emitted only when their text exists. Every caller-owned ID must resolve to an actual description element. |
| requiredIndicator | Optional caller-provided wording displayed only when native required is true. Supply approved localised text as needed. No Arabic translation is invented. |
| Native props | All TextInput props except id are forwarded to the one native input, including ref, value/defaultValue, className, required, readOnly, disabled and handlers. className styles the input, not the wrapper. Explicit lang/dir also apply to the wrapper so labels and feedback follow the field's language and direction. |
| Invalid ownership | errorText establishes an actual invalid state; otherwise caller aria-invalid is preserved. Required or empty does not automatically mean invalid before validation. |
import { FormField } from "@/components/ui/form-field";
<p id="caller-help">Illustrative instructions.</p>
<FormField id="example-field" name="example"
label="Example text" required requiredIndicator="Required"
aria-describedby="caller-help"
helperText="Generic helper description."
errorText={hasError ? "Error: review this example value." : undefined}
defaultValue="Illustrative value" />Foundation and token mapping
| Foundation | Existing tokens and rules |
|---|---|
| Colour | --action-primary-background/text/hover/pressed; --action-secondary-text; --surface-primary/secondary/tertiary; --text-primary/secondary; --border-control; --state-disabled-background/text; --status-error. Decorative orange is not a small-text action fill. |
| Typography | --font-content, --type-ui-size/leading, --type-input-leading, --type-body-size/leading, --weight-regular/medium. English UI 16px/1.4 and field 16px/1.5; Arabic UI 18px/1.5 and field 18px/1.7. Labels/buttons 500; values 400. |
| Spacing | --space-12 block / --space-16 inline comfortable controls; --target-min 48px; --space-08 label/helper and neighbouring-action gaps; --gap-fields-comfortable 24px. No new control-size scale. Compact 8px/16px remains accepted but is not an API in this batch. |
| Geometry | --radius-control 4px; --border-thin 1px; --border-strong 2px for error with compensated padding; --focus-width/offset 2px plus --focus-gap; --focus-ring. No shadow or new shape scale. |
Decorative --border-default is used only on documentation specimen containers, never as the input or secondary-action boundary. The documentation sidebar's solid-orange selected treatment is not a reusable navigation rule.
Accessibility, reflow and bilingual use
Native semantics preserve keyboard behaviour: Space/Enter activate buttons; Enter follows anchors; text inputs support editing and selection. Disabled buttons/inputs are unavailable. Read-only input remains focusable. Visible labels, error words and descriptions provide meaning beyond colour. Focus uses a 2px ring and 2px local-surface gap, with a system Highlight fallback in forced colours.
Wrapping labels and feedback increase height. Inputs stay within available inline width and scroll long single-line values internally. Standalone controls retain 48px minimum targets; inline prose links follow text flow. Leave space for external focus rings and at least 8px between adjacent targets. Do not place groups inside clipping containers.
Set lang="ar" and dir="rtl" for approved Arabic content; existing language aliases select IBM Plex Sans Arabic metrics. Keep English sections explicitly lang="en" and direction appropriate. Use logical CSS placement and isolate known-direction identifiers with bdi in surrounding text. Input direction belongs to the value's meaning, not an automatic script guess. No Arabic specimen copy or translation is supplied here.
Technical direction and CSS checks cannot establish Arabic-language comfort. Native-reader review, assistive-technology checks, actual browser zoom, native forced-colours and real devices remain pending. No comprehensive WCAG conformance is claimed.
Developer integration
Use explicit leaf imports from components/ui. These four files have no filesystem, PDF, documentation-shell, routing or icon dependency. Load the existing fonts.css, tokens.css and typography.css once in the consuming application's stylesheet entry, in that order; use the existing tokens rather than copying a second theme. Global token and language styles are prerequisites; documentation globals are not.
The leaves have no client directive or internal state. React 19 native ref props pass through to their native elements, including Form Field's nested input. Server-render static instances; place interactive callers with handlers, controlled state or refs behind the consuming application's Client Component boundary. Do not pass event handlers from a Server Component. This page's small example module owns only specimen state.
Caller className and style can override presentation and therefore need consumer validation. Do not use them to bypass foundation rules. No broad export package, router adapter, form schema, context provider or extra dependency is introduced.
Validation, sources and approval
These four components and their existing working treatments are internally accepted within the Release 1.0 Approval Candidate scope. Automated checks cover the documented native contracts and representative browser behaviour; their results and exact limitations are recorded in docs/stage-03.2-initial-components.md. Specialist accessibility and Arabic review, real-device and application-specific checks and final company approval remain pending. This is not a universally certified production library.
Internally accepted treatments: secondary hover/active neutral fills; matching primary fill/boundary; underlined action-colour links with unchanged visited palette and standalone UI treatment; read-only secondary surface; caller-owned field IDs and optional required wording. This resolves the initial D-01/D-02 review status for these four components without changing global tokens or source specifications. Future variants and other inventory components remain separate, unimplemented scope.
- Colours: v1.0 pp4,11, actions, disabled and semantic roles.
- Typography: v1.0 pp4,5, field/UI metrics and language handling.
- Spacing and Layout: v1.0 pp2,3, comfortable controls, descriptions and targets. Compact p3 interpretation remains unchanged.
- Geometry: v1.0 pp3,4,9,12, control radius, meaningful/error boundaries, stable geometry and focus.
- Iconography: v1.0 pp3,9,15, semantics and target context. No glyph abstraction or icon-only control is added.
Source: the internally accepted working foundation PDFs in reference/brand-guidelines/, existing central styles, and the Stage 03.1 component inventory. Original sources and all nine foundations remain unchanged. Specialist validation and final company approval remain pending.