Search by

grey-ooo / brand-kit

matthewbaggett

Shared grey.ooo brand kit: a consistent top bar, app switcher and app shell for Grey.ooo Symfony apps.

Package info

git.grey.ooo/Grey.ooo/symfony-brand-kit/

Type:symfony-bundle

pkg:composer/grey-ooo/brand-kit

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

dev-main / 0.1.x-dev 2026-09-17 04:25 UTC

This package is not auto-updated.

Last update: 2026-09-17 13:58:37 UTC


README

grey-ooo/brand-kit — a small Symfony bundle that ships the shared grey.ooo chrome: a consistent top bar, an app switcher, and an app shell (top bar + left sidebar + centered main + footer). Drop it into any Grey.ooo Symfony app so they all wear the same skin.

The chrome is self-contained: all styles are namespaced under .gooo-* and driven by --gooo-* custom properties, so it renders identically whatever CSS/Tailwind setup the host app uses, and needs no build step. It is dark-first, matching the someones.computer aesthetic.

Install

composer require grey-ooo/brand-kit

Register the bundle (config/bundles.php):

GreyOoo\BrandKit\GreyOooBrandKitBundle::class => ['all' => true],

Configure it (config/packages/grey_ooo_brand_kit.yaml) — normally just the one line naming which app this is:

grey_ooo_brand_kit:
    current_app: printshop

The app roster is owned by the bundle, not by consumers: it defaults to GreyOoo\BrandKit\Config\GreyOooApps::ALL, rather than the same list repeated in every repo where the copies drift.

That constant is generated, not hand-edited. An app is in the roster because its own repo on git.grey.ooo carries the grey-ooo-app-roster topic, and its URL is that repo's website field; a nightly job regenerates the constant and opens the PR. Adding an app is a topic on its repo plus a name and icon in tools/roster/presentation.mjs, then a composer update grey-ooo/brand-kit in each consumer. See tools/roster/README.md.

You do not set external per app either. Whether an entry is "another app" depends on who is rendering, so it is derived from current_app: every entry is external except the one being served. Set it explicitly only to force a full page load.

Each entry carries an icon, in one of two forms — the switcher, the brand mark and the styleguide all render it through components/_app_icon.html.twig, so they agree:

Field
iconAn emoji. The default, and enough for an app with no mark of its ownescaped
icon_svgInline SVG, superseding icon for an app that has a mark — someones ships the terminal display it draws in its own appbar, niall the face he talks withprinted raw

Give icon_svg geometry only (a viewBox, no width/height): brand-kit.css sizes it in em so a mark and an emoji sit at one optical scale side by side. It is the only field the kit prints unescaped, so it takes bundle or app config and nothing a request can reach.

An entry may also carry staging_url, which gives its row a blade: a second, amber surface behind the button showing a sliver past the right edge, sliding out from under it on hover (and simply sitting out on touch, where a 4px sliver is not a tap target). It reads as one app with another copy behind it rather than as two apps, which is why it is drawn behind the row and not beside it. The blade lives in the shared roster, so it shows in every app's switcher — a staging box linked here is a shortcut, not a hiding place.

Override the list only when you genuinely need a different one (staging URLs, a local sandbox); apps still accepts a full replacement, and brand still tunes the wordmark:

grey_ooo_brand_kit:
    current_app: printshop
    brand: { name: grey, suffix: .ooo }
    apps:
        - { key: printshop, name: 'PrintShop (staging)', url: 'https://staging.printshop.grey.ooo', icon: '🖨️' }

Expose the bundle's assets to AssetMapper (config/packages/asset_mapper.yaml):

framework:
    asset_mapper:
        paths:
            '%kernel.project_dir%/vendor/grey-ooo/brand-kit/assets': grey-ooo-brand-kit

Then load the CSS + JS. Link the stylesheet in your base template <head> and pull the script in via your importmap.php / app.js (or a <script type="module">):

<link rel="stylesheet" href="{{ asset('grey-ooo-brand-kit/styles/brand-kit.css') }}">

Use

<twig:GreyOoo:Shell current-app="printshop">
    {% block topbar %}
        <twig:GreyOoo:TopBar current-app="printshop">
            {# host app actions: notifications, user menu, … #}
        </twig:GreyOoo:TopBar>
    {% endblock %}

    {% block sidebar %}
        <twig:GreyOoo:NavItem href="{{ path('dashboard') }}" label="Dashboard" :active="true">
            {% block icon %}<twig:ux:icon name="lucide:layout-dashboard"/>{% endblock %}
        </twig:GreyOoo:NavItem>
        {# … #}
    {% endblock %}

    {% block content %}{# page content #}{% endblock %}
    {% block footer %}© grey.ooo{% endblock %}
</twig:GreyOoo:Shell>

Icons are passed via the icon block so the bundle stays independent of any icon set.

The sidebar is optional: omit the sidebar block and the shell renders without a rail and without the drawer toggle, with main spanning the full width. An app with a single page (Niall.chat) has nothing to navigate to, so it leaves the block out.

Components

ComponentPurpose
GreyOoo:ShellPage scaffold — topbar / sidebar (optional) / content / footer blocks.
GreyOoo:TopBarThe grey.ooo bar: mark + wordmark + app switcher, with a right-hand actions slot (default content).
GreyOoo:AppSwitcherGrid-button dropdown of the configured apps.
GreyOoo:NavItemA sidebar link (href, label, active, icon block).

The resolved config is also available in templates as the gooo_brand_kit global.

Style guide

Import the bundle's routes (dev only) to browse a living style guide at /_brand-kit/styleguide:

# config/routes/grey_ooo_brand_kit.yaml
grey_ooo_brand_kit:
    resource: '@GreyOooBrandKitBundle/config/routes.php'