Skip to content

Block icon is lost on a from-scratch generation — and the documented favicon source cannot work #20

Description

@parisek

Problem

BlockJsonGenerator::icon() has exactly two branches:

$existing = $existingBlockJson['icon'] ?? null;
if (is_string($existing) && '' !== $existing) {
    return $existing;              // preserve from disk
}
return $this->loadIcon();          // packaged schemas/block-icon.svg

Preservation-from-disk only fires when a block.json is already there. Whenever it is not — a fresh clone that generates before the file exists, or anyone deleting the generated files and regenerating — every block silently gets the packaged icon instead of the project's own.

The method's own docblock names the consequence:

Without this, fields-generate --root silently rewrites every block's icon to the packaged one on the first run in a project whose brand differs — a 38-file brand regression buried in an otherwise-mechanical normalisation diff.

That guard works, but only while the files are on disk. This is the same structural hole that #18 closed for example: null: a value the generator cannot derive, protected only by a path that does not always run.

$iconPath already exists as a constructor parameter (BlockJsonGenerator:36) and no CLI ever sets it, so in practice only the two branches above are reachable.

Why per-component capture is the wrong fix

icon is deliberately absent from BlockResidualCapturer::CONFIG_SECTIONS, and that is correct. Measured in the reference project: 42 blocks, 1 unique icon, ~2 KB each. Capturing it would duplicate the same SVG into 42 definition files. Unlike example: null — a per-component decision nothing else records — the icon is one project-wide brand asset.

Proposed fix: a project-level icon path

Wire the existing $iconPath through fields-generate, resolved in this order:

  1. an explicit CLI flag (--icon=<path>), for scripted or non-standard layouts;
  2. a conventional project path, if the file exists;
  3. the packaged schemas/block-icon.svg as today's cold-start default.

Only step 2 needs a decision — which conventional path. See below, because the obvious candidate does not work.

The obvious candidate does not work — do not use favicon.svg

wordpress/gutenberg.md § Icon currently tells authors to derive the block icon from the project favicon:

If every block's icon ever needs regenerating from the canonical ${STATIC_PATH}/images/touch/favicon.svg (logo refresh / brand change): strip inkscape / sodipodi / svg namespace declarations + the <sodipodi:namedview> element, replace hardcoded fills (#2d2e36) with currentColor, then compact to one line.

Measured against the reference project, that procedure does not apply to the file it names:

static/images/touch/favicon.svg the icon actually embedded in block.json
size 26 043 B 1 999 B
content a single <image> with a base64 PNG payload real <path> vector artwork
fill attributes none fill="currentColor"
inkscape / sodipodi none

The favicon is a raster wrapped in an SVG envelope. There are no fills to replace and no namespaces to strip, and a base64 PNG cannot take currentColor — so it can never follow the editor's light/dark theme the way the current icon does. Deriving from it would produce a 26 KB blob that renders wrong.

So the embedded icon is a separate, hand-made vector asset that exists nowhere on disk except inside the 42 generated block.json files.

Recommendation: the conventional path should point at a dedicated vector icon — something like ${STATIC_PATH}/images/blocks/block-icon.svg — and adopting this means extracting the current artwork out of block.json into that file once. Do not reach for the favicon.

Secondary: gutenberg.md's icon procedure should be corrected. It describes transforming a file shape (Inkscape-authored vector with hardcoded fills) that at least one real project's favicon does not have, and following it literally would break the icon. That is a tailwind-base doctrine change and belongs in its own chat-first PR.

Validation worth adding

Whatever path is chosen, the generator should reject or warn on an icon that cannot work:

  • contains <image or a data:image/ payload → a raster cannot be recoloured by currentColor;
  • carries hardcoded fill values other than currentColor → will not follow the editor theme.

Both are cheap string checks and both catch precisely the mistake this issue is about.

Not urgent

Today's protection holds because the generated files are on disk in every project. This is a trap for a fresh clone, not a live defect — but it is the kind that surfaces as a 42-file diff nobody reads closely.

Related: #18 (the same structural hole, closed for example: null).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions