Skip to content
This repository was archived by the owner on Feb 19, 2026. It is now read-only.

Commit b9a36bf

Browse files
initredclaude
andcommitted
feat: initial Laravel Boost Guidelines package
- Add boost:guidelines command with interactive selection - Support --all, --tailwindcss, --inertia-react, --force, --list flags - Include Inertia React v2 forms guideline (requires Inertia v2) - Include Tailwind CSS v4 core guideline - Add dependency version checking from composer.lock - Prompt to run boost:update after installation - Add PHPStan (level max) and Pest v4 tests (27 tests) - Add GitHub Actions CI workflow - Add git-cliff for automated changelog generation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 7f7e522 commit b9a36bf

20 files changed

Lines changed: 1564 additions & 0 deletions
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
@php
2+
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
3+
@endphp
4+
## Inertia v2 + React Forms
5+
6+
@if($assist->inertia()->hasFormComponent())
7+
@boostsnippet("`<Form>` Component Example", "react")
8+
import { Form } from '@inertiajs/react'
9+
10+
export default () => (
11+
<Form action="/users" method="post">
12+
{({
13+
errors,
14+
hasErrors,
15+
processing,
16+
wasSuccessful,
17+
recentlySuccessful,
18+
clearErrors,
19+
resetAndClearErrors,
20+
defaults
21+
}) => (
22+
<>
23+
<input type="text" name="name" />
24+
25+
{errors.name && <div>{errors.name}</div>}
26+
27+
<button type="submit" disabled={processing}>
28+
{processing ? 'Creating...' : 'Create User'}
29+
</button>
30+
31+
{wasSuccessful && <div>User created successfully!</div>}
32+
</>
33+
)}
34+
</Form>
35+
)
36+
@endboostsnippet
37+
38+
### Form with shadcn Field Components
39+
- Use Inertia's `<Form>` for state management and shadcn's `Field` components for styling.
40+
- Use Wayfinder's `.form()` method for type-safe action and method attributes.
41+
- Apply `data-invalid` to `Field` and `aria-invalid` to inputs when errors exist.
42+
- Use `FieldError` to display validation messages from Inertia's `errors` object.
43+
44+
@boostsnippet("`<Form>` with Field Components", "react")
45+
import { Form } from '@inertiajs/react'
46+
import { store } from '@/actions/App/Http/Controllers/UserController'
47+
import { Input } from '@/components/ui/input'
48+
import { Button } from '@/components/ui/button'
49+
import { Spinner } from '@/components/ui/spinner'
50+
import {
51+
Field,
52+
FieldGroup,
53+
FieldLabel,
54+
FieldDescription,
55+
FieldError,
56+
} from '@/components/ui/field'
57+
58+
export default () => (
59+
<Form {...store.form()} disableWhileProcessing>
60+
{({ errors, processing }) => (
61+
<FieldGroup>
62+
<Field data-invalid={!!errors.name}>
63+
<FieldLabel htmlFor="name">Full name</FieldLabel>
64+
<Input
65+
id="name"
66+
name="name"
67+
placeholder="John Doe"
68+
aria-invalid={!!errors.name}
69+
/>
70+
{errors.name && <FieldError>{errors.name}</FieldError>}
71+
</Field>
72+
73+
<Field data-invalid={!!errors.email}>
74+
<FieldLabel htmlFor="email">Email</FieldLabel>
75+
<Input
76+
id="email"
77+
name="email"
78+
type="email"
79+
placeholder="john@example.com"
80+
aria-invalid={!!errors.email}
81+
/>
82+
<FieldDescription>We'll never share your email.</FieldDescription>
83+
{errors.email && <FieldError>{errors.email}</FieldError>}
84+
</Field>
85+
86+
<Button type="submit" disabled={processing}>
87+
{processing && <Spinner />}
88+
Create User
89+
</Button>
90+
</FieldGroup>
91+
)}
92+
</Form>
93+
)
94+
@endboostsnippet
95+
@endif
96+
97+
@if($assist->inertia()->hasFormComponent() === false)
98+
{{-- Inertia 2.0.x, not 2.1.0 or higher. So still need to use 'useForm' --}}
99+
@boostsnippet("Inertia React useForm Example", "react")
100+
import { useForm } from '@inertiajs/react'
101+
102+
const { data, setData, post, processing, errors } = useForm({
103+
email: '',
104+
password: '',
105+
remember: false,
106+
})
107+
108+
function submit(e) {
109+
e.preventDefault()
110+
post('/login')
111+
}
112+
113+
return (
114+
<form onSubmit={submit}>
115+
<input type="text" value={data.email} onChange={e => setData('email', e.target.value)} />
116+
{errors.email && <div>{errors.email}</div>}
117+
<input type="password" value={data.password} onChange={e => setData('password', e.target.value)} />
118+
{errors.password && <div>{errors.password}</div>}
119+
<input type="checkbox" checked={data.remember} onChange={e => setData('remember', e.target.checked)} /> Remember Me
120+
<button type="submit" disabled={processing}>Login</button>
121+
</form>
122+
)
123+
@endboostsnippet
124+
@endif
125+
126+
## shadcn/ui Button Component
127+
128+
### Icon Styling
129+
- Do NOT add margin classes (`mr-*`, `ml-*`, `mx-*`) to icons inside buttons. The button uses `gap-2` for spacing.
130+
- Do NOT add size classes (`w-*`, `h-*`, `size-*`) to icons unless a custom size is explicitly needed. The button sets `[&_svg:not([class*='size-'])]:size-4` as the default.
131+
- Icons inside buttons automatically inherit these styles from the button component:
132+
- `pointer-events-none`
133+
- `size-4` (default, ~16px)
134+
- `shrink-0`
135+
136+
### Examples
137+
@verbatim
138+
<code-snippet name="Correct Button with Icon" lang="tsx">
139+
// Correct - no extra classes needed
140+
<Button>
141+
<Mail />
142+
Send Email
143+
</Button>
144+
145+
// Correct - custom size when explicitly needed
146+
<Button>
147+
<Mail className="size-5" />
148+
Send Email
149+
</Button>
150+
</code-snippet>
151+
152+
<code-snippet name="Incorrect Button with Icon" lang="tsx">
153+
// Incorrect - unnecessary margin
154+
<Button>
155+
<Mail className="mr-2" />
156+
Send Email
157+
</Button>
158+
159+
// Incorrect - unnecessary size (default is already size-4)
160+
<Button>
161+
<Mail className="h-4 w-4" />
162+
Send Email
163+
</Button>
164+
</code-snippet>
165+
@endverbatim
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
## Tailwind CSS 4
2+
3+
- Always use Tailwind CSS v4; do not use the deprecated utilities.
4+
- `corePlugins` is not supported in Tailwind v4.
5+
- In Tailwind v4, configuration is CSS-first using the `@theme` directive — no separate `tailwind.config.js` file is needed.
6+
@verbatim
7+
<code-snippet name="Extending Theme in CSS" lang="css">
8+
@theme {
9+
--color-brand: oklch(0.72 0.11 178);
10+
}
11+
</code-snippet>
12+
@endverbatim
13+
- In Tailwind v4, you import Tailwind using a regular CSS `@import` statement, not using the `@tailwind` directives used in v3:
14+
@verbatim
15+
<code-snippet name="Tailwind v4 Import Tailwind Diff" lang="diff">
16+
- @tailwind base;
17+
- @tailwind components;
18+
- @tailwind utilities;
19+
+ @import "tailwindcss";
20+
</code-snippet>
21+
@endverbatim
22+
23+
### Replaced Utilities
24+
- Tailwind v4 removed deprecated utilities. Do not use the deprecated option; use the replacement.
25+
- Opacity values are still numeric.
26+
27+
| Deprecated | Replacement |
28+
|------------+--------------|
29+
| bg-opacity-* | bg-black/* |
30+
| text-opacity-* | text-black/* |
31+
| border-opacity-* | border-black/* |
32+
| divide-opacity-* | divide-black/* |
33+
| ring-opacity-* | ring-black/* |
34+
| placeholder-opacity-* | placeholder-black/* |
35+
| flex-shrink-* | shrink-* |
36+
| flex-grow-* | grow-* |
37+
| overflow-ellipsis | text-ellipsis |
38+
| decoration-slice | box-decoration-slice |
39+
| decoration-clone | box-decoration-clone |
40+
41+
### Size Utilities
42+
- When width and height are equal, use `size-*` instead of separate `w-*` and `h-*` classes.
43+
44+
| Avoid | Prefer |
45+
|-------|--------|
46+
| `w-4 h-4` | `size-4` |
47+
| `w-10 h-10` | `size-10` |
48+
| `w-full h-full` | `size-full` |

.gitattributes

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Auto detect text files and perform LF normalization
2+
* text=auto
3+
4+
# Source files
5+
*.php text eol=lf
6+
*.blade.php text eol=lf
7+
*.json text eol=lf
8+
*.md text eol=lf
9+
*.toml text eol=lf
10+
*.xml text eol=lf
11+
*.yml text eol=lf
12+
*.yaml text eol=lf
13+
14+
# Exclude from export (reduces package size on Packagist)
15+
/.github export-ignore
16+
/.gitattributes export-ignore
17+
/.gitignore export-ignore
18+
/cliff.toml export-ignore
19+
/phpunit.xml export-ignore
20+
/phpstan.neon export-ignore
21+
/tests export-ignore
22+
/CHANGELOG.md export-ignore
23+
/CONTRIBUTING.md export-ignore
24+
/.phpunit.cache export-ignore

.github/workflows/release.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
release:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- name: Checkout
16+
uses: actions/checkout@v4
17+
with:
18+
fetch-depth: 0
19+
20+
- name: Generate changelog
21+
uses: orhun/git-cliff-action@v4
22+
id: git-cliff
23+
with:
24+
config: cliff.toml
25+
args: --current --strip header
26+
env:
27+
OUTPUT: CHANGELOG.md
28+
GITHUB_REPO: ${{ github.repository }}
29+
30+
- name: Create Release
31+
uses: softprops/action-gh-release@v2
32+
with:
33+
body: ${{ steps.git-cliff.outputs.content }}
34+
env:
35+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/tests.yml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: tests
2+
3+
on:
4+
push:
5+
branches:
6+
- dev
7+
- main
8+
pull_request:
9+
branches:
10+
- dev
11+
- main
12+
13+
jobs:
14+
ci:
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
21+
- name: Setup PHP
22+
uses: shivammathur/setup-php@v2
23+
with:
24+
php-version: 8.4
25+
tools: composer:v2
26+
coverage: none
27+
28+
- name: Install Dependencies
29+
run: composer install --no-interaction --prefer-dist --optimize-autoloader
30+
31+
- name: PHPStan Cache
32+
uses: actions/cache@v4
33+
with:
34+
path: /tmp/phpstan
35+
key: ${{ runner.os }}-phpstan-${{ hashFiles('composer.lock') }}
36+
restore-keys: ${{ runner.os }}-phpstan-
37+
38+
- name: Create PHPStan Cache Directory
39+
run: mkdir -p /tmp/phpstan
40+
41+
- name: Tests
42+
run: composer test

.gitignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/vendor/
2+
/node_modules/
3+
.phpunit.result.cache
4+
.php-cs-fixer.cache
5+
composer.lock
6+
.idea/
7+
.vscode/
8+
*.swp
9+
*.swo
10+
.DS_Store
11+
Thumbs.db
12+
coverage/
13+
.phpunit.cache/

CHANGELOG.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
## [1.0.0] - 2025-01-12
11+
12+
### Added
13+
14+
- Initial release
15+
- `boost:guidelines` command with interactive selection
16+
- Inertia React v2 forms guideline with `<Form>` component examples
17+
- shadcn/ui Button component best practices
18+
- Tailwind CSS v4 core utilities guideline
19+
- Dependency checking for Inertia v2 requirements
20+
- `--list` option to view available guidelines
21+
- `--all` option to install all guidelines
22+
- `--force` option to overwrite existing files
23+
24+
[Unreleased]: https://github.com/initred/laravel-boost-guidelines/compare/v1.0.0...HEAD
25+
[1.0.0]: https://github.com/initred/laravel-boost-guidelines/releases/tag/v1.0.0

0 commit comments

Comments
 (0)