BlocksUpdated August 28, 2026

Settings panel

A controlled account and workspace settings form with sectioned fields, validation presentation, submission state, and outcome feedback.

Account & workspacesettings-panel

Compose trustworthy settings without owning product logicLink to section

SettingsPanel renders a sectioned form for account profile and workspace identity fields. It provides responsive structure, accessible field wiring, a native submission boundary, optional progress state, and explicit outcome feedback.

The consuming application owns every value, change callback, validation message, submission, permission, persistence decision, and outcome. The Block does not contact a service or imply that UI validation is server validation.

Source-copy Block

Copy the complete canonical directory into your application. There is no Blocks package, Registry installer, CLI, or automatic update channel for this Block today.

Account settings

Update the profile and workspace details your product displays.

Profile

Choose the details collaborators see in shared work.

Use the name people recognize when working with you.

A short, public-facing description of your role.

Workspace

Keep the shared workspace identity clear and current.

Shown in navigation and shared workspace surfaces.

Canonical identityLink to section

ContractValue
CategoryAccount & workspace
Category slugaccount
Block slugsettings-panel
Sourceapps/marketing/content/blocks/account/settings-panel/
Entry pointapps/marketing/content/blocks/account/settings-panel/index.tsx
ExportSettingsPanel
Informational future Registry identityaccount-settings-panel

The Registry identity is informational only. Registry installation is not available, and this Block does not create a manifest, generated JSON, route, or delivery service.

Install by copying sourceLink to section

  1. Copy the complete settings-panel directory to an application-owned path, such as src/components/blocks/settings-panel/.
  2. Install React 18 or newer and the public @pycolors/ui package.
  3. Load @pycolors/tokens/tokens.css and configure the semantic Tailwind utilities used by the Block.
  4. Import SettingsPanel and its types from the copied local entry point.
account-settings.tsx
"use client";

import * as React from "react";
import {
  SettingsPanel,
  type SettingsPanelSection,
} from "./blocks/settings-panel";

export function AccountSettings() {
  const [displayName, setDisplayName] = React.useState("Alex Morgan");
  const [workspaceName, setWorkspaceName] = React.useState("Northstar");

  const sections = [
    {
      id: "profile",
      title: "Profile",
      fields: [
        {
          id: "display-name",
          name: "displayName",
          label: "Display name",
          value: displayName,
          onValueChange: setDisplayName,
        },
      ],
    },
    {
      id: "workspace",
      title: "Workspace",
      fields: [
        {
          id: "workspace-name",
          name: "workspaceName",
          label: "Workspace name",
          value: workspaceName,
          onValueChange: setWorkspaceName,
        },
      ],
    },
  ] satisfies readonly SettingsPanelSection[];

  return (
    <SettingsPanel
      onSubmit={(event) => {
        event.preventDefault();
        // Validate and persist through your application boundary.
      }}
      sections={sections}
      submitLabel="Save settings"
      title="Account settings"
    />
  );
}

Consumer-owned typesLink to section

settings-panel-types.ts
type SettingsPanelField = Readonly<{
  id: string;
  name: string;
  label: string;
  value: string;
  onValueChange: (value: string) => void;
  description?: string;
  error?: string;
  placeholder?: string;
  autoComplete?: string;
  required?: boolean;
  disabled?: boolean;
}> &
  (
    | Readonly<{
        kind?: "input";
        type?: "text" | "email" | "url" | "tel";
      }>
    | Readonly<{ kind: "textarea"; rows?: number }>
  );

type SettingsPanelSection = Readonly<{
  id: string;
  title: string;
  description?: string;
  fields: readonly SettingsPanelField[];
}>;

type SettingsPanelFeedback = Readonly<{
  status: "success" | "error";
  title: string;
  description?: string;
}>;

Use stable, unique section and field IDs. name remains available to the consumer's form boundary, while the Block prefixes rendered control IDs to avoid collisions between multiple panels.

PropsLink to section

PropTypeDefaultPurpose
titlestringrequiredAccessible panel heading.
descriptionstringnonePanel-level context.
sectionsreadonly SettingsPanelSection[]requiredOrdered consumer-owned sections and controlled fields.
submitLabelstringrequiredReady-state submit action name.
submittingbooleanfalseConsumer-owned busy/disabled truth.
submittingLabelstringSaving…Action copy while submitting.
submitDisabledbooleanfalseConsumer-owned independent action availability.
feedbackSettingsPanelFeedbacknoneConsumer-owned success or error presentation.
onSubmitFormEventHandler<HTMLFormElement>requiredConsumer submission boundary; no persistence is included.
classNamestringnoneRoot composition customization.

Controlled values and validationLink to section

Every field receives a value and onValueChange. The Block never stores a fallback value. Consumer rerenders determine what remains visible.

description becomes helper text. When error is present, the public field primitive displays it, marks the control invalid, and associates the message with the control. The consumer decides when an error exists and what it means. The form uses noValidate deliberately so browser validation never masquerades as the consuming application's validation boundary.

The supported input types are intentionally limited to ordinary profile and workspace presentation. Sensitive credential controls and product-specific widgets are outside this Block.

Submission and feedbackLink to section

onSubmit receives the native form event. The consumer prevents default when appropriate, validates current values, calls its own persistence layer, and controls all subsequent props.

controlled-submission.tsx
<SettingsPanel
  {...settingsProps}
  feedback={
    saveError
      ? { status: "error", title: "Settings were not saved" }
      : saved
        ? { status: "success", title: "Settings saved" }
        : undefined
  }
  onSubmit={handleSubmit}
  submitting={isSaving}
  submittingLabel="Saving account settings…"
/>

While submitting is true, the form exposes aria-busy, editable controls and the action are disabled, and the submitting label is visible. Success feedback uses a polite status region. Error feedback uses an assertive alert. Neither state performs a request, infers permissions, or guarantees persistence.

Responsive behaviorLink to section

  • The panel root and field grids use min-w-0 to remain shrinkable.
  • Section context stacks above its fields on narrow viewports.
  • Sections become a balanced two-column composition at the existing md breakpoint.
  • Fields flow in one column by default and two columns from sm; textareas span the wider field grid.
  • Feedback and the full-width action stack on narrow screens, then align at sm.
  • No JavaScript viewport detection is used.

Accessibility and keyboard behaviorLink to section

  • The root is a labelled region containing one semantic form.
  • Each settings section has a programmatically associated heading.
  • Inputs and textareas use real labels and native focus/keyboard behavior.
  • Descriptions and errors are associated by the public field primitives.
  • The save action is a native submit button with visible focus treatment.
  • submitting exposes busy and disabled semantics.
  • Success/error feedback has visible text and appropriate live-region urgency, rather than relying only on color.

Focused semantic, interaction, and axe tests verify these behaviors. This is not a claim of full WCAG certification.

CustomizationLink to section

After copying, change section content, fields, responsive proportions, action copy, and visual density for the consuming product. Preserve stable IDs, labels, descriptions, invalid-state associations, focus visibility, native form semantics, and consumer ownership while customizing.

Compose separate product surfaces for password changes, security controls, billing, memberships, invitations, or destructive actions. They require different trust, permission, and confirmation boundaries.

Deliberate exclusionsLink to section

This Block does not include password management, MFA, sessions, identity providers, credentials, billing, subscriptions, payments, invitations, team administration, roles, permissions enforcement, authentication, authorization, backend/API calls, persistence, server actions, database models, routing, upgrade gates, or product-specific business logic.

It does not depend on Next.js, Starter Free, Starter Pro, private/deep package imports, a form library, or another runtime dependency. Starter Free informed the general composition only; it remains evidence, not source authority.

Ownership, updates, and rollbackLink to section

After copying, the consumer owns the source, integration, values, validation, submission, persistence, permissions, content, and future maintenance. There is no automatic update or synchronization path. Compare future canonical revisions deliberately and adopt only the changes that fit the application.

Rollback is removal or reversion of the copied settings-panel directory and its local integration. No package version, Registry state, remote Block state, account data, or workspace data is involved.