Skip to content

1.3.0 — Content & Commerce Components

Latest

Choose a tag to compare

@NerdSnipe NerdSnipe released this 19 Jul 10:28

Added

  • DFCalendarView: a new themed month-grid calendar primitive (Supplementary/Calendar/) — single-date selection: Binding<Date>, optional external displayedMonth control, minimumDate/maximumDate bounds with disabled out-of-range days, and a generic @ViewBuilder dayContent: (Date) -> Content slot for event dots/badges. Respects @Environment(\.calendar)/\.locale — no hardcoded first-weekday assumption. Ships one built-in style, .standard.
  • DFEmptyState: a free-tier "no results" primitive (Supplementary/EmptyState/) — icon + title + optional message + optional action button, all independent optionals beyond the required icon/title. Previously this pattern only existed behind DesignFoundationPro's DFEmptyStateBlock.
  • DFCommandPalette: a new Cmd-K-style overlay modifier (Overlays/CommandPalette/) — .dfCommandPalette(isPresented:items:placeholder:onSelect:), case-insensitive substring filtering on title/subtitle, and keyboard navigation on macOS (↑/↓ to highlight, Return to select, Escape to dismiss).
  • DFMaterialTokens wired in: DFTheme.materials: DFMaterialTokens is now a real theme property, and all 16 .glass styles read theme.materials.surfaceMaterial/elevatedMaterial instead of hardcoding .regularMaterial/.thickMaterial. Setting theme.materials.preferLiquidGlass = false opts every .glass style back to its non-glass color-token appearance — useful for accessibility, branding, or pre-26 OS parity testing. DFMaterialTokens itself no longer carries an @available gate (only the individual .glass styles remain iOS/macOS 26+, unchanged).
  • DFComponentTokens expanded: seven new per-component override structs — DFDividerTokens, DFProgressBarTokens, DFSkeletonTokens, DFToggleTokens, DFDatePickerTokens, DFSidebarTokens, DFTabBarTokens — following the existing "every field optional, nil inherits the theme default" pattern. (Slider, Picker, and NavigationBar were evaluated and intentionally excluded — they're thin native-control wrappers with nothing custom-drawn worth exposing as an override.)
  • DFChip: a new themed chip/tag primitive (Primitives/Chip/) — variants .label, .labelWithIcon, .dismissible(onDismiss:), .selectable(isSelected:); styles .filled (default), .tinted, .outlined. Closes a real gap versus competing SwiftUI component libraries (see the dskit competitor profile) that we had no answer for.
  • DFRatingView: a new themed rating primitive (Primitives/Rating/) — .stars (default, half-star support) and .numeric styles; .readOnly and .interactive(onChange:) modes, the latter exposing a VoiceOver-adjustable action.
  • DFPriceView / DFPriceSummaryView: currency-formatted price display (Primitives/Price/, locale-aware via Decimal.formatted(.currency(code:))) with an optional strikethrough compareAtAmount, plus a line-item order-summary layout (Layouts/PriceSummary/) built from DFPriceLineItems with a .total emphasis row.
  • DFEntityRow / DFEntityCard: a new themed "media + title/subtitle + trailing metadata" summary pair (Layouts/EntityRow/) for contacts, orders, and search-result-shaped content — deliberately distinct from DFListRow (which stays a plain structural row with arbitrary leading/trailing views and no styling). DFEntityRow is the list-context form; DFEntityCard is the grid/card-context sibling, built on DFCard.
  • DFGrid / DFCarousel: themed LazyVGrid (.fixed(Int) or .adaptive(minWidth:) columns) and horizontal-scroll (Layouts/Grid/, Layouts/Carousel/) container wrappers — no content opinions, spacing read from DFTheme unless overridden.
  • DFQuantityStepper: a new themed quantity-stepper input (Inputs/Stepper/, named to avoid colliding with SwiftUI's own Stepper) — .bordered (default) and .compact styles, VoiceOver-adjustable.
  • DFEmptyState secondary action: new optional secondaryActionTitle/onSecondaryAction parameters render a second, ghost-styled button alongside the primary one — covers permission-prompt-shaped two-choice screens (e.g. "Allow" / "Not Now") without introducing a near-duplicate component.
  • DFBanner: a new full-width, persistent, inline banner (Supplementary/Banner/) — reuses DFToastSeverity (.info/.success/.warning/.error), optional action button, optional user-dismiss. Deliberately not built on DFToastQueue — toast's floating-capsule/auto-dismiss/global-overlay-queue model doesn't fit a banner's full-width/user-dismissed/inline-in-content shape; conflating the two would have meant bolting a persistent mode onto an auto-timeout-only queue. Place DFBanner directly in your view hierarchy, the same way DFAlert/DFEmptyState work.
  • Five theme presets: .garnet was already shipped in code and CHANGELOG.md, but README.md, CLAUDE.md, AGENTS.md, .cursor/rules/design-foundation.mdc, and the published docs pages still said "four presets" in several places — corrected everywhere to five (.slate, .aurora, .copper, .sage, .garnet).

Fixed

  • Swift 6 strict-concurrency build blocker: DFProgressBarStyle, DFSecureFieldStyle, DFSliderStyle, DFToggleStyle, DFSidebarStyle, and DFDatePickerStyle failed a clean swift build on newer toolchains (their *Style protocol requirements weren't @MainActor, but built-in style implementations called MainActor-isolated SwiftUI statics like .circular/.plain/.switch/Spacer()). Fixed by marking each protocol's view-building requirement @MainActor, matching the pattern already used by DFAlert's closures. Four test files (DFTableTests, DFListTests, DFSidebarTests, DFProgressBarTests, plus the new DFChipTests/DFRatingViewTests) needed the same @MainActor annotation on specific test functions that construct these types directly.
  • Stale DFTextScale test assertion: DFTextTests.swift asserted DFTextScale.allCases.count == 6; the enum has shipped 8 cases (display, title, headline, labelLarge, body, bodySmall, label, caption) since labelLarge/bodySmall were added. Corrected the assertion — unrelated to the concurrency fix above, just test-data drift.

Docs

  • CLAUDE.md, AGENTS.md, and .cursor/rules/design-foundation.mdc updated with reference sections for all of the above, verified against source and mechanically compiled via the doc-snippet CI gate.
  • The dedicated docs/theme-presets/index.html landing page still needs a Garnet swatch card + screenshot asset (docs/images/theme-garnet.png doesn't exist yet) — tracked separately, not done as part of this pass since it needs a real screenshot, not a text fix.