swiftcnearly preview

Styling & composition

Compose string classes or typed utilities over native views.

String classes

Text("Workspace")
    .tw("text-lg font-semibold p-4 bg-surface rounded-lg")

The parser maps documented classes to native styling properties. Those properties generate SwiftUI modifiers in a stable rendering pipeline.

Compose strings with ordinary Swift:

let base = "p-4 rounded-lg"
let selectedStyle = selected ? "bg-primary text-primary-foreground" : "bg-surface"
Text("Workspace").tw("\(base) \(selectedStyle)")

Native values in class strings

let classes: TWClasses = "bg-[\(accent)] rotate-[\(angle)]"
Text("Workspace").tw(cn(classes, "animate-[\(Animation.default)]"), value: angle)

TWClasses preserves native values through interpolation and cn. See typed interpolation for complete examples and runtime string composition.

Typed utilities

Text("Workspace")
    .tw(.text(.lg), .weight(.semibold), .p(4), .bg(.surface), .rounded(.lg))

Strings and typed utilities use the same resolver. Use arbitrary arguments for explicit values.

Composition and overrides

extension TWStyle {
    static var projectCard: TWStyle {
        TWStyle(.card, .p(5), .rounded(.xl))
    }
}

VStack(alignment: .leading) {
    Text("Workspace")
    Text("Three active projects")
}
.tw(.projectCard, .p(8))

Within one .tw(...) call, later base utilities replace earlier values by property. Padding resolves separately for each edge. .p(4), .px(6) produces 16 points vertically and 24 points horizontally. Utility order resolves properties; it does not reorder the rendering pipeline.

Separate .tw(...) calls create separate styled surfaces. For example, .tw(.p(2)).tw(.p(3)) adds both layers of padding. Native modifiers retain their order around each surface.

State variants

Button("Rename", action: rename)
    .buttonStyle(.tw("dropdown-item hover:bg-accent active:scale-[0.98] disabled:opacity-45"))
    .disabled(isLocked)

Hover, focus, pressed, and disabled patches apply after base values, in that order. More specific conditions win before declaration order when precedence is equal.

Variants only affect appearance. Native button styles, such as button-primary, keep Apple's own state appearance. Native .disabled(...) controls whether a control is disabled. See native controls and state.

Validate generated strings

let style = try TWStyle.parse(classes, rules: rules, theme: theme)

Strict parsing throws TWClassError for unknown classes, unknown variants, invalid arguments, and recursive rules.

The convenient view API logs invalid styles through the system logger and leaves the surface undecorated. One invalid class rejects the complete style for that surface, including its global defaults. Use strict parsing in tests to catch mistakes.

parse expands recipes and utility factories against the supplied rules and theme immediately. Registered native modifiers retain references to their names and resolve through the inherited registry during rendering. Use .classes(classes) or a string .tw(classes) to keep reading inherited recipe and style utility definitions when the view renders. See troubleshooting for a complete diagnostic helper and common fixes.

On this page