Vitral 0.2
Referência

Como contribuir

Do que um componente é feito, e onde fica cada peça.

Um componente são cinco peças, cada uma no seu lugar. Nenhuma é opcional, e a ordem é a ordem em que são escritas.

onde cada coisa fica
packages/themes/src/presets/base/components/toggleswitch.ts   # tokens
packages/styles/src/toggleswitch/{toggleswitch.css,index.ts}   # CSS + class map
packages/vue/src/components/ToggleSwitch/
    types.ts            # the props interface, extending BaseProps
    ToggleSwitch.vue    # the component
    ToggleSwitch.spec.ts# axe + keyboard + ARIA
    index.ts
apps/docs/src/demos/ToggleSwitch.vue                           # the demo page

1. Tokens

Uma árvore de tokens cujos valores apontam para a camada semântica ('{formField.borderColor}', '{control.minHeight}'), e não para uma cor. Tudo o que muda de um esquema de cores para o outro vai em colorScheme. Cada token vira uma variável --vt-toggleswitch-….

2. Estilo

  • As classes são vt-toggleswitch, vt-toggleswitch-<part>, vt-toggleswitch-<modifier>.
  • O CSS lê apenas var(--vt-…). Uma cor, um raio ou uma duração fixos no código são um bug.
  • Construa sobre o visual compartilhado (.vt-field, .vt-overlay, .vt-option, .vt-mask) em vez de reestilizá-lo.
  • O foco pelo teclado mostra o anel de foco; tudo o que é animado para sob prefers-reduced-motion.

3. O componente

ToggleSwitch.vue
<script setup lang="ts">
defineOptions({ name: 'VtToggleSwitch' });

const props = withDefaults(defineProps<ToggleSwitchProps>(), {
    unstyled: undefined   // required: an absent boolean prop would be false,
});                       // and false would override the global setting

const { part } = useComponent(toggleswitchStyle, props);
</script>

<template>
    <!-- one call per element: theme classes, pass-through and state -->
    <div v-bind="part('root', state)">
        <span v-bind="part('thumb', state)" />
    </div>
</template>

v-bind="part(…)" em todo elemento é o que torna um componente tematizável, alcançável por pass-through e utilizável sem estilo. Um componente que envolve um controle nativo também usa useSplitAttrs() com inheritAttrs: false, para que class vista o wrapper enquanto name, aria-describedby e o resto chegam ao controle.

4. A demo

Uma página em apps/docs/src/demos/ que exporta meta: DemoMeta de um bloco <script> simples. Este site a encontra por glob e lê o mesmo arquivo de novo, como texto, para mostrar o código ao lado de cada exemplo, então um trecho não tem como se afastar do que documenta.

5. Barrels

pnpm gen escreve todas as listas de index.ts. Ninguém as edita à mão.

Convenções que vale conhecer

  • v-model via defineModel(); ids a partir do useId() do Vue.
  • O texto vem do locale, nunca de um literal no template.
  • Popups usam <Teleport to="body">, a transição vt-overlay e useOverlay(); modais acrescentam useFocusTrap() e lockScroll().
  • Comportamento sem DOM (navegação, parsing, seleção) vai para @vitral/core, com um teste lá.
  • Prefira uma opção a um componente novo: duas coisas que só diferem no que recusam são uma coisa só, com uma prop.

Verificações

terminal
pnpm test                          # vitest + axe
pnpm typecheck                     # vue-tsc over every package and the site
node scripts/gen-index.mjs --check # barrels up to date
pnpm build                         # every package builds, with declarations