agentsclimarketplace

Composability patterns

Skill almasumdev/awesome-mobile-design-system-agent-skills/.github/skills/components/composability-patterns

Agent skills for building and maintaining mobile design systems, tokens, and component libraries.

Install
npx -y skills add almasumdev/awesome-mobile-design-system-agent-skills --skill composability-patterns

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Slots, compound components, and headless primitives for flexible mobile component APIs. Use this when a component needs to serve many callers without becoming a grab-bag of props.

SKILL.md

5.7 KB, as published. Nobody here has run it

Composability Patterns

Instructions

Props scale poorly. A component with 30 props is impossible to evolve. Three patterns let you ship small, stable APIs that still bend to every caller: slots, compound components, and headless primitives.

1. Slots

A slot is a child parameter that the caller fills with arbitrary content. Prefer slots over props whose only purpose is to render something inside the component.

Compose:

@Composable
fun Card(
    modifier: Modifier = Modifier,
    header: @Composable (() -> Unit)? = null,
    footer: @Composable (() -> Unit)? = null,
    content: @Composable ColumnScope.() -> Unit,
) { /* layout */ }

SwiftUI:

public struct DSCard<Header: View, Content: View, Footer: View>: View {
    @ViewBuilder let header: () -> Header
    @ViewBuilder let content: () -> Content
    @ViewBuilder let footer: () -> Footer
    public var body: some View { /* layout */ }
}

Flutter:

class DsCard extends StatelessWidget {
  const DsCard({super.key, this.header, required this.child, this.footer});
  final Widget? header;
  final Widget child;
  final Widget? footer;
}

React Native:

type CardProps = { header?: ReactNode; footer?: ReactNode; children: ReactNode };

2. Compound Components

When parts of a component must share state (open/closed, selected tab, focused field), expose them as sub-components that read from a shared context. The parent owns state; children are dumb consumers.

Compose:

class TabsState internal constructor(val selected: String, val onSelect: (String) -> Unit)
val LocalTabsState = compositionLocalOf<TabsState> { error("Tabs.Root required") }

@Composable
fun TabsRoot(selected: String, onSelect: (String) -> Unit, content: @Composable () -> Unit) {
    CompositionLocalProvider(LocalTabsState provides TabsState(selected, onSelect)) { content() }
}

@Composable
fun TabsList(content: @Composable RowScope.() -> Unit) { Row { content() } }

@Composable
fun TabsTrigger(value: String, label: String) {
    val state = LocalTabsState.current
    Button(onClick = { state.onSelect(value) }, label = label,
           intent = if (state.selected == value) ButtonIntent.Primary else ButtonIntent.Secondary)
}

Usage:

TabsRoot(selected = tab, onSelect = { tab = it }) {
    TabsList {
        TabsTrigger("overview", "Overview")
        TabsTrigger("billing",  "Billing")
    }
}

SwiftUI uses EnvironmentKey, Flutter uses InheritedWidget, React Native uses React Context.

3. Headless Primitives

A headless component ships behavior without styling: focus, keyboard handling, state machine. The design system wraps it with styled variants. This is how you avoid reimplementing combobox, disclosure, dialog, and tooltip correctly on four platforms.

Pattern:

// Headless: no visuals, just state & a11y contract.
class DisclosureState(initialOpen: Boolean = false) {
    var isOpen by mutableStateOf(initialOpen)
        private set
    fun toggle() { isOpen = !isOpen }
}

@Composable
fun Disclosure(state: DisclosureState, trigger: @Composable () -> Unit, panel: @Composable () -> Unit) {
    Column(Modifier.semantics { expanded = state.isOpen }) {
        Box(Modifier.clickable(onClick = state::toggle)) { trigger() }
        if (state.isOpen) panel()
    }
}

Then a styled wrapper supplies visuals and tokens:

@Composable
fun Accordion(title: String, content: @Composable () -> Unit) {
    val state = remember { DisclosureState() }
    Disclosure(
        state = state,
        trigger = { AccordionHeader(title = title, open = state.isOpen) },
        panel   = { AccordionBody { content() } },
    )
}

4. When to Pick Which

NeedPattern
Caller supplies a chunk of content into a known positionSlot
Sub-parts need shared state or a required assembly orderCompound
Behavior is reusable across many visual treatmentsHeadless
Quick one-liner customization (color, size)Variant prop

5. Avoid children Free-For-All

Slots should be named. children inside a Card with implicit ordering creates fragile layouts. Prefer header, media, content, footer as separate slots.

6. Escape Hatches

Every composable primitive should expose a modifier / style passthrough so callers can layer one-off tweaks without forking the component. Never allow overriding a token-bound property (e.g., backgroundColor) — forcing that through a variant prop preserves semantics.

7. Anti-Patterns

  • Dozens of render* props (renderHeader, renderFooter) instead of slots.
  • Sub-components usable outside their parent with silent no-op behavior.
  • A headless hook that leaks visual tokens (colors, paddings).
  • A modifier/style prop that lets callers bypass semantic tokens.

Checklist

  • Content injection uses named slots rather than render* props.
  • Multi-part components share state via context/composition-local/environment — parent owns it.
  • Complex behavior (menu, dialog, combobox) lives in a headless primitive, styled by a wrapper.
  • Every component exposes a modifier / style passthrough for layout-only tweaks.
  • Compound sub-components fail loudly outside their parent.
  • No styling leaks into headless primitives.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.