logo

Paleta - Deixe colorido🎨

Palette — Construtor visual de páginas. Não precisa ser designer.

Demonstração ao vivo Baixar Palette

Scroll

Criando um novo componente do Palette

A practical guide to adding a Single Directory Component (SDC) to Palette: what every component must contain, and how the Style (variant) setting works. See CLAUDE.md for the architecture overview, styles.md for extending a component from another module/theme.


1. A anatomia de um componente

Every component lives in its own folder under components/:

components/<id>/
  <id>.component.yml     # obrigatório — metadados + schema de props/slots
  <id>.twig              # obrigatório — a marcação
  <id>.css               # opcional — estilos co-localizados
  <id>.js                # opcional — comportamento co-localizado
  • <id> is the machine name (lowercase, underscores). Palette exposes the component as canvas_palette:<id>.
  • Core SDC auto-discovers the folder and auto-generates a library core/components.canvas_palette--<id> from the co-located .css/.js. You do not register the library yourself.

1a. <id>.component.yml — as chaves obrigatórias

$schema: https://git.drupalcode.org/project/drupal/-/raw/HEAD/core/assets/schemas/v1/metadata.schema.json

name: My Thing              # rótulo legível mostrado no editor
group: Palette               # categoria na lista de componentes — sempre "Palette"
status: experimental         # experimental | stable | deprecated
description: 'One line describing what it does.'

props:
  type: object
  properties:
    # … see below …

slots:
  # … optional, see 1c …

1b. Props

Cada prop é uma propriedade de JSON-schema. Formas comuns usadas neste kit:

props:
  type: object
  properties:
    title:
      type: string
      title: Title
      examples: ['Default title']        # valor placeholder/semente do editor

    text:
      type: string
      title: Text
      contentMediaType: text/html         # rich text …
      x-formatting-context: block         # … edited with the block CKEditor
      examples: ['<p>Some copy.</p>']

    count:
      type: integer
      title: Count
      default: 3

    image:
      $ref: json-schema-definitions://canvas.module/image
      type: object
      title: Image

Rules that matter (all enforced by Palette — get them wrong and the component silently disables itself and vanishes from the editor):

  • A required: prop MUST have an examples value. If you don't want a default, make the prop optional (leave it out of required:) rather than giving it an empty example.
  • examples[0] is the value seeded onto a newly placed instance — treat it as the editor default/placeholder.
  • default: is the runtime fallback the Twig sees when the prop is unset.
  • Enum props list a meta:enum map for human labels (see §2).
  • Optional integer props error when cleared in the editor; prefer a default: 0 that the Twig treats as "unset" instead of making it clearable.

1c. Slots (componentes contêiner)

Container components (Carousel, Tiles, Stats…) hold child components in static slots, and usually pair with a dedicated *_item component:

slots:
  items:
    title: Items
    description: 'Coloque um My Item por linha.'
    expected: ['canvas_palette:my_item']
    minItems: 1

Slots are rendered in Twig with {% block <slot> %}{% endblock %}. Palette blocks runtime-dynamic slots, so the number of slots is fixed at definition time.

1d. <id>.twig

Twig SDC simples. Props e slots são variáveis/blocos de nível superior:

<div class="cp-my-thing cp-my-thing--{{ style|default('default') }}">
  {% if title %}<h2 class="cp-my-thing__title">{{ title }}</h2>{% endif %}
  {% if text %}<div class="cp-my-thing__body">{{ text }}</div>{% endif %}
  {% block items %}{% endblock %}
</div>

Conventions: BEM class names cp-<id>, cp-<id>__element, cp-<id>--modifier.

1e. JS that uses Drupal.behaviors / once / a third-party library

The auto-generated component library gets core/drupal injected only when the component declares libraryOverrides. So any component whose .js uses Drupal.behaviors, once(), or a shared library must declare its dependencies:

libraryOverrides:
  dependencies:
    - core/drupal
    - core/once
    - canvas_palette/glightbox   # bibliotecas de terceiros são anexadas da mesma forma

Without this, Drupal/once are undefined at runtime. (See tabs, accordion, carousel for real examples.)

1f. Elementos apenas do editor

To show something only inside the Palette editor preview (grab padding, empty outlines…), gate it on the canvas_is_preview flag Palette injects into the Twig context and emit a cp-<id>--preview modifier — it never reaches published pages:

{% set classes = ['cp-my-thing', canvas_is_preview ? 'cp-my-thing--preview' : ''] %}

The JS-side equivalent is window.frameElement?.dataset?.canvasPreview === 'true'.


2. How the Style setting works

The "Style" setting is Palette's single convention for component variants. There is no per-style Twig or JS branching — it is one enum prop turned into a CSS class.

2a. The style prop

Most components declare a single enum string prop named style (labelled "Style", or sometimes "Layout" / "Columns"):

    style:
      type: string
      title: Style
      default: default
      enum:
        - default
        - colorful
      meta:enum:
        default: Default
        colorful: Colorful
      examples: [default]
  • enum — the machine values.
  • meta:enum — the human labels shown in the select.
  • default / examples — the starting value.

2b. O Twig interpola isso em um modificador BEM

The Twig writes the value verbatim onto the root element as a modifier class:

<div class="cp-my-thing cp-my-thing--{{ style|default('default') }}">

So style: colorful renders class="cp-my-thing cp-my-thing--colorful".

2c. O CSS usa o modificador como chave

Each style is just a CSS rule set keyed on .cp-<id>--<value>:

.cp-my-thing--colorful {
  background: linear-gradient(135deg, #0973f2, #7333e5);
  color: #fff;
}

That is the entire mechanism: enum value → cp-<id>--<value> class → CSS.

2d. Ela flutua para o topo do formulário do editor

canvas_palette_move_style_prop_first() gives any prop literally named style a very low #weight, so the Style selector always appears first in the component-inputs sidebar. This runs from canvas_palette_form_component_instance_form_alter() for every sdc.canvas_palette.* component — you get it for free just by naming the prop style. (Components with a custom inputs-form handler that re-weights props must still leave style at the top.)

2e. Adicionando um novo valor de Style a um componente existente

Adding an enum value is an additive, safe change:

  1. Add the value to enum: and a label to meta:enum:.
  2. Add the .cp-<id>--<value> CSS.
  3. Rebuild (see §3). The new value appears on newly placed instances and after a hard editor reload; existing instances keep their pinned version.

Shared style conventions in this kit (see CLAUDE.md for the exact token values): a colorful value and an optional eyebrow kicker prop, styled to the landing-page design system. Reuse the hard-coded token values there rather than inventing new colours.


3. Build e pegadinhas de versionamento

After adding or editing a component, rebuild from the site root:

php vendor/drush/drush/drush.php cr

On this OSPanel stack a CLI drush cr often does not reach the browser — flush through the web (admin Configuration → Development → Performance → Clear all caches, or a throwaway web/flush.php). See CLAUDE.md → "Local environment quirk".

Canvas freezes each component's schema as a versioned config entity (sdc.canvas_palette.<id>). The consequences:

  • Editing *.component.yml does not live-update placed instances. New props / enum values show up on newly placed instances and after a hard editor reload.
  • Additive changes are safe (new enum value, new optional prop). Changing an existing prop's field type is not — it throws and disables the whole component. Revert the type to restore it.
  • If a component ends up disabled after a change, regenerate and re-enable:

    \Drupal::service('Drupal\canvas\ComponentSource\ComponentSourceManager')
      ->generateComponents();
    $c = \Drupal::entityTypeManager()->getStorage('component')
      ->loadUnchanged('sdc.canvas_palette.<id>');
    if ($c && !$c->status()) { $c->enable()->save(); }

    Verify it stays enabled across a second generate — a fix that only survives one manual re-enable is not a real fix.


Checklist para um novo componente

components/<id>/<id>.component.yml with $schema, name, group: Palette, status, description.

Props defined; every required prop has examples.

<id>.twig with cp-<id> BEM classes.

 A style enum prop (+ meta:enum) if the component has variants, wired to cp-<id>--{{ style }} and matching CSS.

libraryOverrides.dependencies if the JS uses Drupal.behaviors / once / a third-party library.

Slots + a paired *_item component if it's a container.

Rebuilt (through the web); component shows ENABLED across two generates.