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
36 changes: 36 additions & 0 deletions .github/workflows/comment-pr.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Called by the "Comment on PR" step in playwright-e2e.yml via actions/github-script.
// Required env vars (injected by the workflow):
// JOB_STATUS – the value of ${{ job.status }}

module.exports = async ({ github, context }) => {
const { owner, repo } = context.repo;
const runId = context.runId;
const runUrl = `https://github.com/${owner}/${repo}/actions/runs/${runId}`;
const status = process.env.JOB_STATUS;
const emoji = status === 'success' ? '✅' : '❌';
const statusText = status === 'success' ? 'All tests passed' : 'Some tests failed';

const body = [
`## ${emoji} Playwright E2E — ${statusText}`,
'',
`| Detail | Link |`,
`|--------|------|`,
`| Workflow run | [View run](${runUrl}) |`,
`| HTML report | Download the **playwright-report** artifact from the run page above |`,
`| Traces & videos | Download the **playwright-results** artifact (attached when tests fail) |`,
'',
`> **How to view the HTML report locally:**`,
`> 1. Download and unzip the \`playwright-report\` artifact.`,
`> 2. Run \`bunx playwright show-report <path-to-report>\` to open it in your browser.`,
'',
`> Traces and videos are captured **on first retry** (failures only).`,
].join('\n');

// Always post a new comment so each commit push gets its own status entry
await github.rest.issues.createComment({
owner,
repo,
issue_number: context.issue.number,
body,
});
};
78 changes: 78 additions & 0 deletions .github/workflows/playwright-e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
name: Playwright E2E

on:
pull_request:
types: [opened, synchronize, reopened]
workflow_dispatch:

concurrency:
group: e2e-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
e2e:
timeout-minutes: 30
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write # needed for the PR comment step

steps:
- uses: actions/checkout@v4

# Astro requires Node >=22.12.0 (see .node-version / package.json engines)
- uses: actions/setup-node@v4
with:
node-version-file: ".node-version"

- uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: Install dependencies
run: bun install --frozen-lockfile

# Install only Chromium in CI – matches playwright.config.ts project list
- name: Install Playwright browsers
run: bunx playwright install --with-deps chromium

# Build the Astro site and start the preview server on port 4321
- name: Build Astro site
run: bun run build

- name: Run Playwright E2E tests
run: bunx playwright test
env:
CI: "true"
PLAYWRIGHT_BASE_URL: "http://localhost:4321"

# Always upload artifacts so failures can be investigated
- name: Upload Playwright report
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
retention-days: 14

- name: Upload test results (traces, videos, screenshots)
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-results
path: test-results/
retention-days: 14

# Comment on the PR with run status and artifact links.
# Uses github-script so it works from fork PRs without leaking secrets
# (GITHUB_TOKEN is scoped to the target repo).
- name: Comment on PR
if: always() && github.event_name == 'pull_request'
uses: actions/github-script@v7
env:
JOB_STATUS: ${{ job.status }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const script = require('./.github/workflows/comment-pr.js')
await script({ github, context, core })
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,7 @@ pnpm-debug.log*

# Maestro Setup
.maestro/

# E2E Tests - Playwright
test-results/
playwright-report/
9 changes: 9 additions & 0 deletions .opencode/learnings/registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,13 @@ All captured learnings from the AI Diamond Chain are registered here.
- **Source:** User request for tmux skill for detached session control
- **Summary:** Created tmux-automation skill with commands for creating detached sessions, sending keys programmatically, capturing output, and session management
- **Location:** `.opencode/skills/tmux-automation/SKILL.md`
- **Confidence:** 🟢 High

---

## 2026-04-18 - DaisyUI v5 Custom Theme Configuration
- **Type:** skill
- **Source:** Implementation Diamond - Custom theme creation for PodCodar brand
- **Summary:** Learned DaisyUI v5's new `@plugin "daisyui/theme"` syntax with OKLCH color format, created light/dark theme pair with PodCodar brand colors (tech blues + warm amber accents), disabled default themes for clean implementation
- **Location:** `.opencode/skills/daisyui-v5-themes/SKILL.md`, `src/styles/global.css`
- **Confidence:** 🟢 High
191 changes: 191 additions & 0 deletions .opencode/skills/daisyui-v5-themes/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
# DaisyUI v5 Custom Theme Configuration

Creating custom themes in DaisyUI v5 (2026 best practices).

## Overview

DaisyUI v5 uses a new `@plugin` syntax for defining custom themes with OKLCH color format for better perceptual uniformity.

## Important: Verify Brand Assets First!

⚠️ **Always check actual brand assets (logos, mascots, brand guidelines) before defining colors.**

Don't assume colors based on industry stereotypes (e.g., "tech company = blue"). Extract colors from:
- Logo files (check for fill attributes or visual colors)
- Brand mascot images
- Existing brand guidelines
- Official website colors

**Example:** PodCodar is a tech community, but their llama mascot is **purple/violet**, not blue!

## Syntax

```css
@import "tailwindcss";

/* Disable default themes */
@plugin "daisyui" {
themes: false;
}

/* Define custom theme */
@plugin "daisyui/theme" {
name: "my-theme";
default: true;
prefersdark: false;
color-scheme: light;

/* Colors */
--color-base-100: oklch(98% 0.005 260);
--color-base-200: oklch(96% 0.01 260);
--color-base-300: oklch(90% 0.015 260);
--color-base-content: oklch(20% 0.02 260);

--color-primary: oklch(55% 0.18 250);
--color-primary-content: oklch(98% 0.005 260);

--color-secondary: oklch(70% 0.15 70);
--color-secondary-content: oklch(20% 0.02 70);

--color-accent: oklch(65% 0.18 195);
--color-accent-content: oklch(98% 0.005 260);

--color-neutral: oklch(40% 0.03 260);
--color-neutral-content: oklch(98% 0.005 260);

--color-info: oklch(70% 0.15 240);
--color-info-content: oklch(20% 0.02 240);

--color-success: oklch(65% 0.15 145);
--color-success-content: oklch(98% 0.005 145);

--color-warning: oklch(75% 0.15 85);
--color-warning-content: oklch(20% 0.02 85);

--color-error: oklch(60% 0.18 25);
--color-error-content: oklch(98% 0.005 25);

/* Design tokens */
--radius-selector: 0.5rem;
--radius-field: 0.375rem;
--radius-box: 0.75rem;

--size-selector: 0.25rem;
--size-field: 0.25rem;

--border: 1px;
--depth: 1;
--noise: 0;
}
```

## Key Concepts

### Color Format: OKLCH

OKLCH is preferred over hex/RGB for:
- Better perceptual uniformity
- Easier to adjust lightness without changing perceived hue
- Consistent contrast ratios across color wheel

Format: `oklch(L% C H)` where:
- L = Lightness (0-100%)
- C = Chroma (0-0.4 typical range)
- H = Hue (0-360 degrees)

### Theme Properties

| Property | Description |
|----------|-------------|
| `name` | Theme identifier (kebab-case) |
| `default` | Set as default theme |
| `prefersdark` | Auto-activate on `prefers-color-scheme: dark` |
| `color-scheme` | Browser UI color (light/dark) |

### Color Variables

**Base Layers (Backgrounds)**
- `--color-base-100`: Main background
- `--color-base-200`: Secondary background (cards, hover)
- `--color-base-300`: Tertiary background (borders, dividers)
- `--color-base-content`: Primary text color

**Semantic Colors**
- `--color-primary`: Main brand color (buttons, links)
- `--color-secondary`: Complementary accent
- `--color-accent`: Highlight/CTA color
- `--color-neutral`: Grayscale base

**Status Colors**
- `--color-info`: Informational states
- `--color-success`: Success states
- `--color-warning`: Warning states
- `--color-error`: Error states

Each semantic/status color has a `-content` variant for text/icons on that background.

### Design Tokens

| Token | Description |
|-------|-------------|
| `--radius-selector` | Checkboxes, toggles, badges |
| `--radius-field` | Buttons, inputs, tabs |
| `--radius-box` | Cards, modals, alerts |
| `--size-selector` | Checkbox/toggle base size |
| `--size-field` | Button/input base size |
| `--border` | Global border width |
| `--depth` | 3D depth effect (0-1) |
| `--noise` | Noise texture overlay (0-1) |

## Best Practices

1. **Disable default themes** when using custom themes (`themes: false`)
2. **Define both light and dark variants** for complete coverage
3. **Use consistent hue families** across base layers (e.g., all blue-tinted)
4. **Ensure contrast ratios** meet WCAG AA (4.5:1 for text)
5. **Test with real components** using the DaisyUI theme generator
6. **Keep chroma low** for base layers (0.005-0.03) to avoid color casts
7. **Higher chroma** for primary/accent colors (0.15-0.25) for vibrancy

## Dark Mode Pattern

```css
/* Light theme (default) */
@plugin "daisyui/theme" {
name: "my-light";
default: true;
prefersdark: false;
color-scheme: light;
/* ... */
}

/* Dark theme (auto-switch) */
@plugin "daisyui/theme" {
name: "my-dark";
default: false;
prefersdark: true;
color-scheme: dark;
/* Reverse base layers, adjust for dark */
--color-base-100: oklch(18% 0.02 260);
--color-base-200: oklch(23% 0.025 260);
--color-base-300: oklch(30% 0.03 260);
--color-base-content: oklch(95% 0.015 260);
/* Brighter primaries for dark mode */
--color-primary: oklch(70% 0.15 250);
/* ... */
}
```

## Theme Generator Tool

Use https://daisyui.com/theme-generator/ to:
- Visually design themes
- Export OKLCH values
- Preview component variants
- Copy-paste ready CSS

## References

- [DaisyUI Themes Docs](https://daisyui.com/docs/themes/)
- [DaisyUI Theme Generator](https://daisyui.com/theme-generator/)
- [OKLCH Color Picker](https://oklch.com/)
1 change: 1 addition & 0 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
},
"formatter": {
"enabled": true,
"useEditorconfig": true,
"indentStyle": "tab",
"indentWidth": 2,
"lineWidth": 100,
Expand Down
8 changes: 8 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading