Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 59 additions & 59 deletions .agents/skills/add-component/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,19 @@ Use for standalone components with variant-based styling (e.g., Button, Badge, I
**File:** `packages/core/src/components/{component-name}.tsx`

```tsx
import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"

const componentVariants = cva("astw:base-classes", {
variants: {
variant: { default: "astw:..." },
size: { default: "astw:..." },
},
defaultVariants: { variant: "default", size: "default" },
});
})

type ComponentProps = React.ComponentProps<"div"> & VariantProps<typeof componentVariants>;
type ComponentProps = React.ComponentProps<"div"> & VariantProps<typeof componentVariants>

function Component({ className, variant, size, ...props }: ComponentProps) {
return (
Expand All @@ -39,10 +39,10 @@ function Component({ className, variant, size, ...props }: ComponentProps) {
className={cn(componentVariants({ variant, size, className }))}
{...props}
/>
);
)
}

export { Component, componentVariants, type ComponentProps };
export { Component, componentVariants, type ComponentProps }
```

### Pattern B — Compound Component (Namespace Object)
Expand All @@ -52,24 +52,24 @@ Use for multi-part components wrapping Base UI (e.g., Dialog, Sheet, Tooltip, Co
**File:** `packages/core/src/components/{component-name}.tsx`

```tsx
import * as React from "react";
import { ComponentName as BaseComponentName } from "@base-ui/react/component-name";
import { cn } from "@/lib/utils";
import * as React from "react"
import { ComponentName as BaseComponentName } from "@base-ui/react/component-name"
import { cn } from "@/lib/utils"

// Pick only stable, consumer-relevant props — prevents upstream breakage
type RootProps = Pick<
React.ComponentProps<typeof BaseComponentName.Root>,
"open" | "defaultOpen" | "onOpenChange"
> & { children: React.ReactNode };
> & { children: React.ReactNode }

function Root({ children, ...props }: RootProps) {
return (
<BaseComponentName.Root data-slot="component-name" {...props}>
{children}
</BaseComponentName.Root>
);
)
}
Root.displayName = "ComponentName.Root";
Root.displayName = "ComponentName.Root"

function Content({
className,
Expand All @@ -84,15 +84,15 @@ function Content({
>
{children}
</BaseComponentName.Content>
);
)
}
Content.displayName = "ComponentName.Content";
Content.displayName = "ComponentName.Content"

// Assemble all sub-components into a single namespace object
export const ComponentName = {
Root,
Content,
};
}
```

### Pattern C — Multi-File Component (Directory)
Expand All @@ -110,8 +110,8 @@ components/
**index.ts:**

```tsx
export { ComponentName, default } from "./ComponentName";
export type { ComponentNameProps } from "./types";
export { ComponentName, default } from "./ComponentName"
export type { ComponentNameProps } from "./types"
// DO NOT export internal types, type guards, or enums
```

Expand All @@ -137,14 +137,14 @@ components/
**component-name.tsx** — Internal compound parts (Pattern B style, not exported from `index.ts`):

```tsx
import * as React from "react";
import { ComponentName as BaseComponentName } from "@base-ui/react/component-name";
import { cn } from "@/lib/utils";
import * as React from "react"
import { ComponentName as BaseComponentName } from "@base-ui/react/component-name"
import { cn } from "@/lib/utils"

function ComponentNameRoot<Value>({
...props
}: React.ComponentProps<typeof BaseComponentName.Root<Value>>) {
return <BaseComponentName.Root data-slot="component-name" {...props} />;
return <BaseComponentName.Root data-slot="component-name" {...props} />
}

function ComponentNameItem({
Expand All @@ -157,32 +157,32 @@ function ComponentNameItem({
className={cn("astw:...", className)}
{...props}
/>
);
)
}

// Assemble into Parts object
const ComponentNameParts = {
Root: ComponentNameRoot,
Item: ComponentNameItem,
// ...other sub-components
};
}

export { ComponentNameRoot, ComponentNameItem, ComponentNameParts };
export { ComponentNameRoot, ComponentNameItem, ComponentNameParts }
```

**component-name-standalone.tsx** — Public standalone + Parts:

```tsx
import { ComponentNameRoot, ComponentNameItem, ComponentNameParts } from "./component-name";
import type { MappedItem } from "./select-standalone"; // shared type if applicable
import { ComponentNameRoot, ComponentNameItem, ComponentNameParts } from "./component-name"
import type { MappedItem } from "./select-standalone" // shared type if applicable

interface ComponentNameStandaloneProps<I> {
items: I[];
placeholder?: string;
mapItem?: (item: ExtractItem<I>) => MappedItem;
className?: string;
value?: ExtractItem<I> | null;
onValueChange?: (value: ExtractItem<I> | null) => void;
items: I[]
placeholder?: string
mapItem?: (item: ExtractItem<I>) => MappedItem
className?: string
value?: ExtractItem<I> | null
onValueChange?: (value: ExtractItem<I> | null) => void
}

function ComponentNameStandalone<I>(props: ComponentNameStandaloneProps<I>) {
Expand All @@ -193,17 +193,17 @@ function ComponentNameStandalone<I>(props: ComponentNameStandaloneProps<I>) {
{/* pre-wired sub-components */}
</ComponentNameRoot>
</div>
);
)
}

// Use Object.assign to keep the standalone callable as a component
// while attaching Parts and variant sub-components
const ComponentName = Object.assign(ComponentNameStandalone, {
Parts: ComponentNameParts,
// Optional variant sub-components (e.g., Async, Creatable)
});
})

export { ComponentName };
export { ComponentName }
```

**Consumer usage:**
Expand Down Expand Up @@ -255,13 +255,13 @@ const gridClasses = cn(
columns === 4
? "astw:@[400px]:grid-cols-2 astw:@[600px]:grid-cols-3 astw:@[800px]:grid-cols-4"
: "astw:@[400px]:grid-cols-2 astw:@[600px]:grid-cols-3",
);
)

return (
<div className="astw:@container">
<div className={gridClasses}>{/* content */}</div>
</div>
);
)
```

Do NOT write custom CSS with `@container` rules or use inline styles for container queries.
Expand All @@ -275,14 +275,14 @@ const STYLES = `
@container (min-width: 400px) {
.custom-grid { grid-template-columns: repeat(2, 1fr); }
}
`;
`

return (
<>
<style dangerouslySetInnerHTML={{ __html: STYLES }} />
<div className="custom-container">{/* content */}</div>
</>
);
)
```

## Step 3: Base UI Integration
Expand Down Expand Up @@ -332,14 +332,14 @@ If the same nested shape is reused across multiple components, extract a shared
When using Base UI's `useRender` hook (for polymorphic rendering):

```tsx
import { useRender } from "@base-ui/react/use-render";
import { useRender } from "@base-ui/react/use-render"

function Component({ render, children, ...props }: ComponentProps) {
return useRender({
defaultTagName: "button",
render,
props: { "data-slot": "component", children, ...props },
});
})
}
```

Expand All @@ -349,13 +349,13 @@ Add the export to `packages/core/src/index.ts`:

```tsx
// Simple component — export component, variants, and Props type
export { Component, componentVariants, type ComponentProps } from "./components/component-name";
export { Component, componentVariants, type ComponentProps } from "./components/component-name"

// Compound component (namespace object) — export only the namespace
export { ComponentName } from "./components/component-name";
export { ComponentName } from "./components/component-name"

// Directory component — export component and Props type only
export { ComponentName, type ComponentNameProps } from "./components/component-name";
export { ComponentName, type ComponentNameProps } from "./components/component-name"
```

**Minimal surface rules:**
Expand All @@ -370,30 +370,30 @@ export { ComponentName, type ComponentNameProps } from "./components/component-n
Create a test file next to the component: `{component-name}.test.tsx`

```tsx
import { afterEach, describe, expect, it, vi } from "vitest";
import { cleanup, render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { ComponentName } from "./component-name";
import { afterEach, describe, expect, it, vi } from "vitest"
import { cleanup, render, screen } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
import { ComponentName } from "./component-name"

afterEach(() => {
cleanup();
});
cleanup()
})

describe("ComponentName", () => {
// Snapshot tests for each variant/state
describe("snapshots", () => {
it("default", () => {
const { container } = render(<ComponentName>Content</ComponentName>);
expect(container.innerHTML).toMatchSnapshot();
});
});
const { container } = render(<ComponentName>Content</ComponentName>)
expect(container.innerHTML).toMatchSnapshot()
})
})

// Behavioral tests
it("renders correctly", () => {
render(<ComponentName>Content</ComponentName>);
expect(screen.getByText("Content")).toBeDefined();
});
});
render(<ComponentName>Content</ComponentName>)
expect(screen.getByText("Content")).toBeDefined()
})
})
```

**Testing conventions:**
Expand Down
13 changes: 6 additions & 7 deletions .agents/skills/create-changeset/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,13 @@ The file format is:
Add `DescriptionCard` component for displaying structured field data in a responsive grid layout.

```tsx
import { DescriptionCard } from "@tailor-platform/app-shell";

<DescriptionCard
import { DescriptionCard } from "@tailor-platform/app-shell"
;<DescriptionCard
fields={[
{ label: "Name", value: "Alice" },
{ label: "Email", value: "alice@example.com" },
]}
/>;
/>
```
````

Expand All @@ -113,13 +112,13 @@ Replace `defaultResourceRedirectPath` with `redirectToResource()` helper for mod
Before:

```tsx
defineModule({ defaultResourceRedirectPath: "/dashboard" });
defineModule({ defaultResourceRedirectPath: "/dashboard" })
```

After:

```tsx
import { redirectToResource } from "@tailor-platform/app-shell";
defineModule({ redirect: redirectToResource("dashboard") });
import { redirectToResource } from "@tailor-platform/app-shell"
defineModule({ redirect: redirectToResource("dashboard") })
```
````
4 changes: 0 additions & 4 deletions .oxfmtrc.json

This file was deleted.

15 changes: 15 additions & 0 deletions .oxfmtrc.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"ignorePatterns": [".github/workflows/*.lock.yml", "**/CHANGELOG.md"],
"overrides": [
{
"files": ["**/*.md"],
"options": {
// Disable semicolons in Markdown code blocks. Without this, oxfmt
// appends a semicolon to single-line JSX expressions (e.g. `<Button />;`),
// which renders as visible ";" text in md-react-preview live previews.
"semi": false,
},
},
],
}
Loading