Skip to content
Merged
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
64 changes: 64 additions & 0 deletions .github/workflows/draft-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Draft PR

on:
push:
branches:
- feat/*
- fix/*
- refactor/*
- chore/*
- docs/*

permissions:
contents: read
pull-requests: write

jobs:
draft-pr:
name: Draft PR
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: false

- name: Generate Scout app token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.SCOUT_APP_ID }}
private-key: ${{ secrets.SCOUT_PRIVATE_KEY }}
permission-contents: read
permission-pull-requests: write

- name: Create draft PR if not exists
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
BRANCH: ${{ github.ref_name }}
run: |
PLAN_ID=$(echo "$BRANCH" | grep -oP '[a-z]+-\d+' | head -1 || true)
if [ -z "$PLAN_ID" ]; then
echo "Branch '$BRANCH' does not match plan-ID pattern — skipping PR creation"
exit 0
fi

TITLE=$(echo "$BRANCH" | sed 's|^[^/]*/||' | sed 's/^[a-z]*-[0-9]*-//' | sed 's/-/ /g' | sed 's/\b\(.\)/\u\1/g')
DISPLAY_ID=$(echo "$PLAN_ID" | sed 's/^\([a-z]*\)/\U\1/' | sed 's/\-[a-z]/\U&/')
FULL_TITLE="${DISPLAY_ID}: ${TITLE}"

EXISTING=$(gh pr list --head "$BRANCH" --state all --json number --jq '.[0].number' 2>/dev/null || echo "")

if [ -z "$EXISTING" ]; then
BODY="**Plan:** \`${DISPLAY_ID}\` — see \`papercamp/ideas/${DISPLAY_ID}.md\` for the full idea and its plan."
BODY="${BODY}"$'\n\n'"This PR was auto-created as a draft on the first push to this feature branch."
BODY="${BODY}"$'\n'"Promote to \"Ready for review\" when the plan reaches \`review\` status."
gh pr create \
--title "$FULL_TITLE" \
--body "$BODY" \
--draft \
--base main
echo "Created draft PR for $FULL_TITLE"
else
echo "PR #$EXISTING already exists for branch '$BRANCH' — skipping"
fi
14 changes: 13 additions & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,17 @@
"formatter": {
"trailingCommas": "none"
}
}
},
"overrides": [
{
"include": ["src/components/select/**", "src/components/modal/**"],
"linter": {
"rules": {
"a11y": {
"useSemanticElements": "off"
}
}
}
}
]
}
11 changes: 2 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
"version": "0.1.1",
"description": "Glossy, noisy, cosy React component library — frosted glass, film grain, and lava glow",
"type": "module",
"sideEffects": [
"**/*.css",
"**/*.scss"
],
"sideEffects": ["**/*.css", "**/*.scss"],
"packageManager": "pnpm@10.12.1",
"main": "dist/index.cjs",
"module": "dist/index.mjs",
Expand Down Expand Up @@ -96,10 +93,6 @@
"vite-plugin-dts": "^4.5.4"
},
"pnpm": {
"onlyBuiltDependencies": [
"@biomejs/biome",
"@swc/core",
"esbuild"
]
"onlyBuiltDependencies": ["@biomejs/biome", "@swc/core", "esbuild"]
}
}
22 changes: 22 additions & 0 deletions papercamp/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,27 @@
"initializedAt": "2026-08-04T14:52:35.357Z",
"nextId": {
"idea": 9
},
"defaultAgents": {
"phase": {
"agent": "claude-code",
"model": "opus",
"effort": "high"
},
"planDraft": {
"agent": "claude-code",
"model": "opus",
"effort": "high"
},
"ideaExtend": {
"agent": "claude-code",
"model": "opus",
"effort": "high"
},
"commitSuggest": {
"agent": "claude-code",
"model": "sonnet",
"effort": "low"
}
}
}
16 changes: 8 additions & 8 deletions papercamp/ideas/IDEA-2.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
id: IDEA-2
title: Tier 2 — form controls and overlays
type: feat
status: planned
status: review
created: 2026-08-04
tags:
- components
Expand All @@ -14,10 +14,10 @@ The medium-usage band from the paper-camp inventory: Input (8 files), Modal (6),
API conventions from observed usage: Input/Textarea keep DOM-event `onChange` (native-like); Select takes an `options` array and value-based `onChange` (matching how all 7 call sites use it). Modal ships compound parts — `Modal.Body`, `Modal.Footer`, `Modal.Error` — because the identical body/footer/error-line scaffolding is copy-pasted across all six paper-camp modals today.

### Phases
- [ ] Input + Textarea (label, helperText, error, sm/md)
- [ ] Select (options array, controlled value, keyboard nav, hidden native select for forms)
- [ ] Checkbox + Switch
- [ ] Modal (open/onClose/title/size, focus trap, scroll lock, Escape) with Body/Footer/Error compound parts
- [ ] ListItem (active, icon, trailing action slot)
- [ ] Alert (semantic variants, dismissible) + Divider (orientation, label)
- [ ] Spinner + Progress + Skeleton
- [x] Input + Textarea (label, helperText, error, sm/md)
- [x] Select (options array, controlled value, keyboard nav, hidden native select for forms)
- [x] Checkbox + Switch
- [x] Modal (open/onClose/title/size, focus trap, scroll lock, Escape) with Body/Footer/Error compound parts
- [x] ListItem (active, icon, trailing action slot)
- [x] Alert (semantic variants, dismissible) + Divider (orientation, label)
- [x] Spinner + Progress + Skeleton
7 changes: 4 additions & 3 deletions papercamp/ideas/IDEA-4.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ Every component shipped in IDEA-1..3 needs an entry in the showcase's Components

### Phases
- [x] Tier 1 entries: IconButton, Stamp, Tooltip, Toast (+ extend Button entry with danger/icons/isActive)

### Log
- 2026-08-04 — IconButton and Stamp entries live (with variant/size/dot controls, clickable-stamp counter demo); Button entry gained the danger option; Card entry shows sm/md sizes plus a keyboard-accessible clickable card. Tooltip/Toast entries pending their components.
- [ ] Tier 2 entries: form controls, Modal, feedback components
- [ ] Tier 3 entries: gap-fillers + icon-set grid

### Log
- 2026-08-04 — IconButton and Stamp entries live (with variant/size/dot controls, clickable-stamp counter demo); Button entry gained the danger option; Card entry shows sm/md sizes plus a keyboard-accessible clickable card. Tooltip/Toast entries now live too.
- 2026-08-04 — Structure fix: an earlier Log insertion accidentally split the Phases list, orphaning the Tier 2/3 phases inside the Log — the idea parsed as one checked phase. Restored all three phases under Phases.
89 changes: 89 additions & 0 deletions src/components/alert/alert.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
@use '../../styles/tokens' as *;
@use '../../styles/mixins' as *;

.alert {
display: flex;
align-items: flex-start;
gap: $space-2;
padding: $space-3;
background-color: var(--fui-status-neutral-fill);
color: var(--fui-text);
font-family: $font-family-sans;
font-size: $font-size-sm;
line-height: 1.4;
border-left: 4px solid var(--fui-status-neutral-text);
}

.icon {
display: inline-flex;
flex-shrink: 0;
align-items: center;
margin-top: 0.1em;
}

.message {
flex: 1;
min-width: 0;
}

.dismiss {
display: inline-flex;
flex-shrink: 0;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
margin: -0.25rem -0.25rem -0.25rem 0;
border: none;
padding: 0;
background: transparent;
color: inherit;
font-size: 1.125rem;
line-height: 1;
cursor: pointer;
opacity: 0.55;

&:hover {
opacity: 1;
}

&:focus-visible {
@include focus-ring;
}
}

.info {
background-color: var(--fui-status-info-fill);
border-left-color: var(--fui-status-info-text);

.icon {
color: var(--fui-status-info-text);
}
}

.success {
background-color: var(--fui-status-success-fill);
border-left-color: var(--fui-status-success-text);

.icon {
color: var(--fui-status-success-text);
}
}

.warning {
background-color: var(--fui-status-warning-fill);
border-left-color: var(--fui-status-warning-text);

.icon {
color: var(--fui-status-warning-text);
}
}

.error {
background-color: var(--fui-status-error-fill);
border-left-color: var(--fui-status-error-text);

.icon {
color: var(--fui-status-error-text);
}
}
60 changes: 60 additions & 0 deletions src/components/alert/alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import type { HTMLAttributes, ReactNode } from 'react';
import { cn } from '../../utils/style-helpers';
import styles from './alert.module.scss';

export type AlertVariant = 'info' | 'success' | 'warning' | 'error';

export interface AlertProps extends HTMLAttributes<HTMLDivElement> {
variant?: AlertVariant;
dismissible?: boolean;
onDismiss?: () => void;
children: ReactNode;
}

const icons: Record<AlertVariant, ReactNode> = {
info: (
<svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor" aria-hidden="true">
<path d="M8 0a8 8 0 100 16A8 8 0 008 0zm1 12H7V7h2v5zM8 5.5A1.25 1.25 0 118 3a1.25 1.25 0 010 2.5z" />
</svg>
),
success: (
<svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor" aria-hidden="true">
<path d="M8 0a8 8 0 100 16A8 8 0 008 0zm3.7 5.7l-4.4 4.4a1 1 0 01-1.4 0L3.7 8.3l1.4-1.4L6.6 8.4l3.7-3.7 1.4 1z" />
</svg>
),
warning: (
<svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor" aria-hidden="true">
<path d="M7.1 1.5a1 1 0 011.8 0l6.4 12A1 1 0 0114.4 15H1.6a1 1 0 01-.9-1.5l6.4-12zM7 6v4h2V6H7zm0 5v2h2v-2H7z" />
</svg>
),
error: (
<svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor" aria-hidden="true">
<path d="M8 0a8 8 0 100 16A8 8 0 008 0zm3 10.6L9.4 12 8 10.6 6.6 12 5 10.6 6.6 9 5 7.4 6.6 6 8 7.4 9.4 6 11 7.4 9.4 9 11 10.6z" />
</svg>
),
};

export function Alert({
variant = 'info',
dismissible = false,
onDismiss,
className,
children,
...props
}: AlertProps) {
return (
<div
role={variant === 'error' || variant === 'warning' ? 'alert' : 'status'}
className={cn(styles.alert, styles[variant], className)}
{...props}
>
<span className={styles.icon}>{icons[variant]}</span>
<div className={styles.message}>{children}</div>
{dismissible && (
<button type="button" aria-label="Dismiss" className={styles.dismiss} onClick={onDismiss}>
×
</button>
)}
</div>
);
}
2 changes: 2 additions & 0 deletions src/components/alert/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { Alert } from './alert';
export type { AlertProps, AlertVariant } from './alert';
Loading
Loading