Labelled is a foundational content trait used by Organization, Product, Relationship, and other core objects to provide consistent text display across all view contexts. Supports configurable length constraints, required/optional description, and copy variants (e.g., primary, short, formal) for context-sensitive text rendering.

Fields it adds

FieldTypeRequiredDescription
label string yes Human-readable display name rendered in primary surfaces.
description string no Supporting description used in detail and card contexts.
placeholder string no Hint copy surfaced in form fields when the label is empty.

What it shows in each context

list
LabelCell
detail
DetailHeader
form
FormLabelGroup
card
CardHeader
timeline
TimelineEntryLabel
inline
InlineLabel

Parameters

maxLabelLength number
Maximum character length for the label field. Enforced at schema validation and echoed to UI components for truncation.
maxDescriptionLength number
Maximum character length for the description field. Controls form input limits and detail view truncation.
requireDescription boolean
When true, description becomes a required field. Useful for objects where supporting text is essential (e.g., Knowledge Articles).
copyVariants string[]
Named copy variants available for context-sensitive rendering. "primary" is the default label. Additional variants (e.g., "short", "formal") can be used for list truncation, exports, or formal documents.

Objects that use it

The trait file

traits/content/Labelled.trait.yaml
trait:
  name: Labelled
  version: 2.0.0
  description: |
    Canonical label, description, and placeholder copy for authored objects.
    Labelled is a foundational content trait used by Organization, Product,
    Relationship, and other core objects to provide consistent text display
    across all view contexts.

    Supports configurable length constraints, required/optional description,
    and copy variants (e.g., primary, short, formal) for context-sensitive
    text rendering.
  category: content
  tags:
    - label
    - description
    - copy
    - metadata
    - content

parameters:
  - name: maxLabelLength
    type: number
    required: false
    description: Maximum character length for the label field. Enforced at schema validation and echoed to UI components for truncation.
    default: 80
    validation:
      minimum: 1
      maximum: 255
  - name: maxDescriptionLength
    type: number
    required: false
    description: Maximum character length for the description field. Controls form input limits and detail view truncation.
    default: 160
    validation:
      minimum: 1
      maximum: 1000
  - name: requireDescription
    type: boolean
    required: false
    description: |
      When true, description becomes a required field. Useful for objects
      where supporting text is essential (e.g., Knowledge Articles).
    default: false
  - name: copyVariants
    type: string[]
    required: false
    description: |
      Named copy variants available for context-sensitive rendering.
      "primary" is the default label. Additional variants (e.g., "short", "formal")
      can be used for list truncation, exports, or formal documents.
    default:
      - primary

schema:
  label:
    type: string
    required: true
    description: Human-readable display name rendered in primary surfaces.
    validation:
      minLength: 1
      maxLengthFromParameter: maxLabelLength
      pattern: "^\\S.*\\S$|^\\S$"
  description:
    type: string
    required: false
    requiredFromParameter: requireDescription
    description: Supporting description used in detail and card contexts.
    validation:
      maxLengthFromParameter: maxDescriptionLength
  placeholder:
    type: string
    required: false
    description: Hint copy surfaced in form fields when the label is empty.
    validation:
      maxLength: 80

semantics:
  label:
    semantic_type: text.label
    token_mapping: tokenMap(text.label.*)
    ui_hints:
      component: TextHeading
      maxLengthParameter: maxLabelLength
      headingLevel: 2
  description:
    semantic_type: text.description
    token_mapping: tokenMap(text.body.*)
    ui_hints:
      component: TextBody
      maxLengthParameter: maxDescriptionLength
  placeholder:
    semantic_type: text.placeholder
    token_mapping: tokenMap(text.placeholder.*)
    ui_hints:
      component: FormHint
      maxLength: 80

view_extensions:
  list:
    - component: LabelCell
      priority: 100
      props:
        field: label
        descriptionField: description
        truncate: true
        maxLengthParameter: maxLabelLength
  detail:
    - component: DetailHeader
      position: top
      priority: 100
      props:
        titleField: label
        subtitleField: description
        headingLevel: 1
  form:
    - component: FormLabelGroup
      position: before
      priority: 100
      props:
        labelField: label
        descriptionField: description
        placeholderField: placeholder
        maxLabelLengthParameter: maxLabelLength
        maxDescriptionLengthParameter: maxDescriptionLength
        requireDescriptionParameter: requireDescription
  card:
    - component: CardHeader
      position: top
      priority: 100
      props:
        titleField: label
        supportingField: description
  timeline:
    - component: TimelineEntryLabel
      props:
        field: label
        compact: true
  inline:
    - component: InlineLabel
      props:
        field: label
        maxLength: 40

tokens:
  # Label (heading) tokens
  text.label.color: "var(--sys-text-strong)"
  text.label.font-weight: "var(--sys-font-weight-semibold)"
  text.label.font-size: "var(--sys-font-size-lg)"
  text.label.line-height: "var(--sys-line-height-tight)"
  # Description (body) tokens
  text.body.color: "var(--sys-text-subtle)"
  text.body.font-weight: "var(--sys-font-weight-regular)"
  text.body.font-size: "var(--sys-font-size-sm)"
  text.body.line-height: "var(--sys-line-height-relaxed)"
  # Placeholder (hint) tokens
  text.placeholder.color: "var(--sys-text-muted)"
  text.placeholder.font-weight: "var(--sys-font-weight-regular)"
  text.placeholder.font-size: "var(--sys-font-size-sm)"
  text.placeholder.line-height: "var(--sys-line-height-normal)"

dependencies: []

metadata:
  created: "2025-10-12"
  updated: "2026-02-28"
  owners:
    - design@oods.systems
    - uxcopy@oods.systems
  maturity: stable
  accessibility:
    keyboard: FormLabelGroup fields are focusable in reading order with standard tab navigation.
    screenreader: |
      Label is announced as a heading (level configurable via headingLevel prop).
      Description is announced as supplementary text associated with the heading.
      Placeholder is announced as input hint via aria-placeholder.
  regionsUsed:
    - list
    - detail
    - form
    - card
    - timeline
    - inline
  examples:
    - Organization
    - Product
    - Relationship
    - Ticket
    - Knowledge Article
  references:
    - "Trait Engine Spec v0.1 §2 — foundational trait definitions"
    - "objects/core/Organization.object.yaml — consumer"
    - "objects/core/Product.object.yaml — consumer"
    - "objects/core/Relationship.object.yaml — consumer"