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
142 changes: 142 additions & 0 deletions .github/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<p align="center">
<a href="https://pollora.dev">
<img src="https://raw.githubusercontent.com/Pollora/.github/main/brand/banners/theme-default.png" width="100%" alt="Pollora Default Theme: the Blade starter theme for Pollora">
</a>
</p>

<p align="center">
<a href="https://github.com/Pollora/theme-default/tags"><img src="https://img.shields.io/github/v/tag/Pollora/theme-default?label=version" alt="Version"></a>
<a href="../LICENSE"><img src="https://img.shields.io/github/license/Pollora/theme-default" alt="License"></a>
</p>

The starter theme every [Pollora](https://pollora.dev) project begins with: Blade templates, Vite with hot reload, Tailwind CSS v4 and a block editor styled from the same design tokens as the page. It is the template `php artisan pollora:make:theme` downloads by default, so a new theme starts from working code instead of an empty folder.

<p align="center">
<img src="https://pollora.dev/press/theme-default.png" width="100%" alt="The Pollora default theme">
</p>

## Installation

A new Pollora project generates this theme for you. In an existing project, run:

```bash
php artisan pollora:make:theme my-theme
```

Choose "Default" when asked for a template (or pass `--repository=Pollora/theme-default`). The command downloads the latest tag, fills in the theme's name and namespace, runs `npm install` and `npm run build`, and offers to activate the theme.

Requirements: a Pollora project (PHP 8.4+ and WordPress 7.1+ for a new one) and Node.js 20.19+ or 22.12+ (Vite 8) for the asset build.

## Quick start

```bash
cd themes/my-theme
npm run dev # Vite dev server with hot reload
npm run build # production assets
```

Templates live in `resources/views` (`index`, `home`, `single`, `page`, `404`…), Gutenberg blocks in `resources/views/blocks` (`hero`, `call-to-action`), and theme settings in `config/` (menus, sidebars, supports, image sizes).

## Design tokens

The design lives in the `@theme static` block of `resources/assets/css/app.css`:
colors, type scale and radii, with concrete values. `npm run build` writes
them into the `theme.json` the editor reads
(`public/build/theme/<slug>/assets/theme.json`), so the page and the editor
always offer the same palette and sizes.

- Change a token in `app.css`, not in `theme.json`: the root `theme.json` is
only the base (layout, fonts, block styles), and a slug defined there wins
over `@theme`.
- The font is the exception: Inter (the variable font, `InterVariable.woff2`) needs a `fontFace` declaration, which only
`theme.json` can hold, so fonts are declared there and `vite.config.js` turns
their generation off (`disableTailwindFonts`). The same option exists for
colors, font sizes and radii.
- Spacing and layout widths are not generated: set them in `theme.json`.

See [Theme.json and Vite Build Integration](https://pollora.dev/theming/theme-structure/)
for the details.

## Gutenberg design system

Every core block is styled in `theme.json` (`styles`: root, elements, blocks),
from the presets above only, so a paragraph, a quote, a table or a button look
the same in the editor and on the page. Block-specific touches that
`theme.json` has no property for (the quote's gradient border, the table's
rules) sit in each block's `css` field, which the editor loads too.

- Reference presets by the name WordPress prints: `5xl` becomes
`var(--wp--preset--font-size--5-xl)`. Never a Tailwind variable
(`var(--text-xl)`): it does not exist in the editor.
- A block's `css` takes one selector per rule; WordPress wraps it in
`:root :where(...)` and breaks on `a, b`.
- `app/Cms/StyleLayers.php` puts WordPress's CSS in cascade layers, declared at
the top of `app.css`: `theme, base, wp-core, wp, components, utilities`. The
block library and the global styles beat Tailwind's reset, and a Tailwind
class in a Blade template always beats them. A link in the templates that
should not look like a content link says so with `no-underline`.
- `php bin/tests/run.php` checks that every preset and custom variable the
styles use exists.

## Documentation

- [Themes](https://pollora.dev/theming/theme-structure/): generating a theme, its structure, `theme.json` and the Vite build
- [Assets and Vite](https://pollora.dev/theming/assets-vite/)
- [Gutenberg blocks](https://pollora.dev/blocks/gutenberg-blocks/)

## Template development

This repository is a template: its files carry placeholders that `pollora:make:theme` substitutes, so it does not run as is. Develop in a Pollora test project under the code name **`pollora-starter`**, unique enough that packaging cannot replace anything by accident.

1. Generate the dev theme in your test project:

```bash
php artisan pollora:make:theme pollora-starter \
--repository=Pollora/theme-default \
--theme-author="Pollora" \
--theme-author-uri="https://pollora.dev" \
--theme-uri="https://pollora.dev" \
--theme-description="Pollora starter theme" \
--theme-version="1.0.0"
```

2. Develop in `themes/pollora-starter/`.

3. Package the changes back into this repository, then review them:

```bash
./bin/package-theme.sh /path/to/your-project/themes/pollora-starter
git diff
```

4. Verify by regenerating the theme from the new tag once it is published:

```bash
rm -rf themes/pollora-starter
php artisan pollora:make:theme pollora-starter --repository=Pollora/theme-default
```

### Placeholders

`bin/package-theme.sh` turns the dev values into these placeholders, and `pollora:make:theme` replaces them:

| Placeholder | Replaced with |
|---|---|
| `%theme_name%` | Theme slug (e.g. `my-theme`) |
| `%theme_namespace%` | PSR-4 namespace (e.g. `Theme\MyTheme`) |
| `%theme_uri%` | Theme URL |
| `%theme_author%` | Author name |
| `%theme_author_uri%` | Author URL |
| `%theme_description%` | Theme description |
| `%theme_version%` | Version number |

- Always use `pollora-starter` as the dev theme name: the packaging script depends on it, and fails if the code name survives anywhere.
- `bin/` is removed when `pollora:make:theme` downloads the theme. It holds the packaging script and the CI checks (`php bin/tests/run.php`).

## Contributing

Contributions are welcome: see the [contributing guide](https://github.com/Pollora/.github/blob/main/CONTRIBUTING.md). Report security issues privately, as described in the [security policy](https://github.com/Pollora/.github/blob/main/SECURITY.md).

## License

The Pollora Default Theme is open-source software licensed under the [MIT license](../LICENSE). © [RuBee group](https://rubee.group)
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2025 RuBee group
Copyright (c) 2025 RuBee group (https://rubee.group)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
129 changes: 29 additions & 100 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,118 +1,47 @@
# Pollora Default Theme
# %theme_name%

The default theme template for [Pollora](https://pollora.dev). This repository contains placeholder files that are processed by `pollora:make-theme` when creating a new project.
%theme_description%

## For End Users
Built with [Pollora](https://pollora.dev) from the [theme-default](https://github.com/Pollora/theme-default) template.

You don't interact with this repository directly. When you create a Pollora project, the theme is generated automatically:
## What's inside

```bash
composer create-project pollora/pollora my-project
# or manually:
php artisan pollora:make-theme my-theme
```

## Design tokens

The design lives in the `@theme static` block of `resources/assets/css/app.css`:
colours, type scale and radii, with concrete values. `npm run build` writes
them into the `theme.json` the editor reads
(`public/build/theme/<slug>/assets/theme.json`), so the page and the editor
always offer the same palette and sizes.

- Change a token in `app.css`, not in `theme.json`: the root `theme.json` is
only the base (layout, fonts, block styles), and a slug defined there wins
over `@theme`.
- The font is the exception: Inter (the variable font, `InterVariable.woff2`) needs a `fontFace` declaration, which only
`theme.json` can hold, so fonts are declared there and `vite.config.js` turns
their generation off (`disableTailwindFonts`). The same option exists for
colours, font sizes and radii.
- Spacing and layout widths are not generated: set them in `theme.json`.

See [Theme.json and Vite Build Integration](https://pollora.dev/theming/theme-structure/)
for the details.

## Gutenberg design system

Every core block is styled in `theme.json` (`styles`: root, elements, blocks),
from the presets above only, so a paragraph, a quote, a table or a button look
the same in the editor and on the page. Block-specific touches that
`theme.json` has no property for (the quote's gradient border, the table's
rules) sit in each block's `css` field, which the editor loads too.

- Reference presets by the name WordPress prints: `5xl` becomes
`var(--wp--preset--font-size--5-xl)`. Never a Tailwind variable
(`var(--text-xl)`): it does not exist in the editor.
- A block's `css` takes one selector per rule; WordPress wraps it in
`:root :where(...)` and breaks on `a, b`.
- `app/Cms/StyleLayers.php` puts WordPress's CSS in cascade layers, declared at
the top of `app.css`: `theme, base, wp-core, wp, components, utilities`. The
block library and the global styles beat Tailwind's reset, and a Tailwind
class in a Blade template always beats them. A link in the templates that
should not look like a content link says so with `no-underline`.
- `php bin/tests/run.php` checks that every preset and custom variable the
styles use exists.

## Contributing

### Development Setup

Theme development happens in a Pollora test project using the code name **`pollora-starter`**. This name is unique enough to avoid accidental replacements during packaging.

1. **Generate the dev theme** in your test project:

```bash
php artisan pollora:make-theme pollora-starter \
--theme-author="Pollora" \
--theme-author-uri="https://pollora.dev" \
--theme-uri="https://pollora.dev" \
--theme-description="Pollora starter theme" \
--theme-version="1.0.0"
```

2. **Develop** in `themes/pollora-starter/` — modify views, CSS, config, etc.

3. **Package** your changes back into this repository:

```bash
cd /path/to/theme-default
./bin/package-theme.sh /path/to/your-project/themes/pollora-starter
%theme_name%/
├── app/ # PHP classes, namespace %theme_namespace% (providers, CMS integrations)
├── config/ # menus, sidebars, supports, image sizes, Gutenberg, login screen
├── resources/
│ ├── assets/ # CSS (Tailwind CSS v4, design tokens in app.css), JS, fonts, images
│ └── views/ # Blade templates (index, home, single, page, 404…)
│ ├── blocks/ # Gutenberg blocks, registered automatically
│ └── patterns/ # Block patterns
├── functions.php # Registers the theme with Pollora
├── style.css # WordPress theme header
├── theme.json # Base editor settings; the build adds the design tokens
└── vite.config.js # Asset build
```

The script replaces all `pollora-starter` / `PolloraStarter` / `%theme_namespace%` references with the appropriate `%placeholder%` tokens.
## Commands

4. **Review, commit, tag and push**:
Run from `themes/%theme_name%`:

```bash
git diff
git add -A && git commit -m "feat: description of changes"
git tag x.y.z
git push origin main --tags
npm run dev # Vite dev server with hot reload
npm run build # production assets
```

5. **Verify** by regenerating the theme from the updated tag:
From the project root:

```bash
rm -rf themes/pollora-starter
php artisan pollora:make-theme pollora-starter ...
php artisan pollora:make:block my-block --theme=%theme_name% # a new Gutenberg block
php artisan discovery:clear # after adding attribute-based classes
php artisan pollora:doctor # when something fails without an error
```

### Placeholders

The following placeholders are replaced by `pollora:make-theme`:

| Placeholder | Replaced with |
|---|---|
| `pollora-starter` | Theme slug (e.g. `my-theme`) |
| `%theme_namespace%` | PSR-4 namespace (e.g. `Theme\MyTheme`) |
| `https://pollora.dev` | Theme URL |
| `Pollora` | Author name |
| `https://pollora.dev` | Author URL |
| `Pollora starter theme` | Theme description |
| `1.0.0` | Version number |
Change colors, font sizes and radii in the `@theme static` block of `resources/assets/css/app.css`, not in `theme.json`: `npm run build` writes them into the `theme.json` the editor reads.

### Important
## Read more

- Always use `pollora-starter` as the dev theme name — the packaging script depends on it
- Never commit files with concrete theme names (check with `grep -r "pollora-starter" --include="*.php" --include="*.css"` before pushing)
- The `bin/` directory is excluded when the theme is downloaded by `pollora:make-theme`
- [Themes](https://pollora.dev/theming/theme-structure/): structure, template hierarchy, `theme.json` and the Vite build
- [Assets and Vite](https://pollora.dev/theming/assets-vite/)
- [Gutenberg blocks](https://pollora.dev/blocks/gutenberg-blocks/)
29 changes: 28 additions & 1 deletion bin/package-theme.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,42 @@ rsync -av --delete \
--exclude='.git' \
--exclude='package-theme.sh' \
--exclude='bin/' \
--exclude='/README.md' \
--exclude='/.github/' \
--exclude='/LICENSE' \
--exclude='/license.txt' \
"$SOURCE/" "$TARGET_DIR/" \
--quiet

# package.json comes from the development copy, which declares no license (or
# another one): the template's is MIT, the same as LICENSE.
echo "Setting the package.json license to MIT..."
node -e '
const fs = require("fs");
const [file, license] = process.argv.slice(1);
const pkg = JSON.parse(fs.readFileSync(file, "utf8"));
let out = pkg;
if ("license" in pkg) {
pkg.license = license;
} else {
out = {};
for (const [key, value] of Object.entries(pkg)) {
out[key] = value;
if (key === "private") out.license = license;
}
if (!("license" in out)) out.license = license;
}
fs.writeFileSync(file, JSON.stringify(out, null, 4) + "\n");
' "$TARGET_DIR/package.json" "MIT"

echo "Replacing code name with placeholders..."

# README.md documents the packaging workflow itself, so it names the code name
# on purpose. Every other file must come out of here carrying placeholders only.
find "$TARGET_DIR" -type f \
-not -path "*/.git/*" \
-not -path "*/.github/*" \
-not -path "*/bin/*" \
-not -path "*/node_modules/*" \
-not -name "package-theme.sh" \
-not -name "README.md" \
Expand Down Expand Up @@ -93,7 +120,7 @@ find "$TARGET_DIR" -type f \
echo "Checking nothing kept the code name..."

if leaked=$(grep -rn "${CODE_NAME}\|${CODE_STUDLY}" "$TARGET_DIR" \
--exclude-dir=.git --exclude-dir=node_modules --exclude=README.md --exclude=package-theme.sh); then
--exclude-dir=.git --exclude-dir=.github --exclude-dir=bin --exclude-dir=node_modules --exclude=README.md --exclude=package-theme.sh); then
echo ""
echo "Error: the code name survived packaging:"
echo "$leaked"
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"private": true,
"license": "MIT",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
Loading