Sistema de componentes sem estilos · Novo recurso

Sistema de design Kobalte.

Kobalte fornece componentes sem interface visual para Solid, incluindo campos numéricos com valores brutos e formatados separados, a anatomia composta de caixas de seleção e botões com estado pressionado. Este guia conecta esses primitivos aos sinais do Solid por meio de um ajustador de receitas com quantidades derivadas e caixas de seleção de ingredientes que podem ser redefinidas.

Guia OpenDesign9 capítulos29 entradas de referênciaGuia de aplicação original
01

Prévia da interface do sistema de design Kobalte

Ajuste as quantidades dos ingredientes, reúna os itens e fixe uma receita original localmente.

Cartão de receita reativoAbrir exemplo completo
Kobalte 0.13.14 com Solid 1.9.15; primitivas headless nativas e CSS original completo. · Referência de implementação
02

Referência de design do sistema de design Kobalte

Kobalte 0.13.14 oferece primitivas headless do Solid, mas não uma escala visual canônica. Cada cor e dimensão recipe-* abaixo faz parte da configuração completa original do aplicativo. Os nomes API e os atributos de estado foram verificados no pacote instalado e na documentação oficial dos componentes.

Cores semânticas originais

Fonte
Nome / RegraValor / ConfiguraçãoUso
--recipe-canvas#f6f2ec Área externa
--recipe-surface#fffefa Painéis de receita e porções
--recipe-ink#403a30 Texto principal
--recipe-muted#807465 Texto de apoio
--recipe-line#e6ded2 Divisórias e bordas
--recipe-action#95612f Checkbox selecionado e texto da ação
--recipe-on-action#fffTexto em primeiro plano da ação preenchida
--recipe-focus#b88955 Foco visível pelo teclado
--recipe-soft#f1e4d3 Fundo do controle de incremento e do alfinete pressionado

Tipografia e dimensões originais

Fonte
Nome / RegraValor / ConfiguraçãoUso
Bodysystem-ui, sans-serifSem download de fontes
Page heading34px / 1.15; 29px narrowHierarquia original
Recipe titleGeorgia, serif; 30px / 1.2Destaque editorial original
Ingredient copy13px / 1.5; 12px narrowRótulos com quebra de linha legível
--recipe-space-1 / 2 / 34px / 8px / 12pxPequenos espaçamentos
--recipe-space-4 / 6 / 816px / 24px / 32pxEspaçamento dos painéis e da página
Panel radius16pxGeometria original das superfícies
Checkbox control21 × 21px; 5px radiusIndicador original visível
Stepper trigger42px wideDisparador nativo com a geometria original
Layout breakpoint760pxDuas colunas passam a formar uma
Focus ring3px; 3px offsetTratamento original do foco pelo teclado

Contrato de estado do componente nativo

Fonte
Nome / RegraValor / ConfiguraçãoUso
NumberField.rawValuenumberEstado aritmético em vez de texto formatado
NumberField.onRawValueChange(value: number) => voidAtualiza as quantidades e limpa as seleções desatualizadas
minValue / maxValue / step1 / 8 / 1Limites da aplicação repassados ao campo numérico nativo
formatOptions.maximumFractionDigits0Exibição de porções inteiras
validationStatevalid | invalidValidade da aplicação refletida no campo nativo
Checkbox.checked / onChangeboolean / callbackSeleção controlada de ingredientes
Checkbox data attributesdata-checked; data-focus-visibleEstilize os estados nativos de seleção e foco pelo teclado
ToggleButton.pressed / onChangeboolean / callbackControle local de fixação
ToggleButton data-pressedpresent when pressedEstilo de fixação local ativada
03

Guia completo do sistema de design Kobalte

Um guia prático para escolher, aplicar e avaliar este sistema em uma interface real.

DESIGN-kobalte.md

Orientações completas, um ponto de partida para a implementação e links para o código-fonte.

Baixar o guia

Conecte componentes do sistema de design Kobalte a sinais do Solid Kobalte

Conecte os componentes Kobalte aos Signals do Solid

O componente usa sinais Solid para as porções, os ingredientes separados e a fixação. createMemo deriva a validade do sinal numérico, e For renderiza uma coleção estável de ingredientes. As propriedades controladas de Kobalte leem esses sinais diretamente; as atualizações não exigem um provedor de formulário separado nem um adaptador de estado no estilo de React.

Com base neste guia de OpenDesign

NumberField, Checkbox e ToggleButton são importados dos respectivos pontos de entrada dos componentes. Cada um tem uma função diferente: o campo numérico recebe entradas numéricas editáveis, cada caixa de seleção representa um ingrediente separado, e o botão de alternância controla a fixação da receita. As regras de cálculo e invalidação ficam em funções da aplicação, em vez de serem incorporadas a seletores visuais.

Consulte a especificação de apoio

Sistema de design Kobalte: mantenha o estado numérico separado do texto formatado

Mantenha o estado numérico separado do texto formatado

NumberField recebe rawValue={portions()} e onRawValueChange={update}. O sinal numérico direciona a multiplicação, enquanto o campo lida com a formatação e a edição pelo usuário. Usar texto de exibição formatado nos cálculos introduziria suposições sobre separadores ou localidade que a primitiva nativa já resolve.

O exemplo permite de uma a oito porções inteiras, com incremento de uma unidade e maximumFractionDigits definido como zero. Um predicado calculado à parte ainda verifica se o valor é finito, inteiro e está dentro dos limites, pois um campo editável pode conter temporariamente um valor incompleto ou inválido. Enquanto o valor for inválido, os totais exibem um travessão e as caixas de seleção dos ingredientes ficam desativadas. A interface não apresenta uma quantidade plausível, mas incorreta, como se o usuário tivesse informado um valor válido.

Consulte a especificação de apoio

Sistema de design Kobalte: componha o campo com seu rótulo e suas mensagens nativos

Componha o campo com seu rótulo e suas mensagens nativos

O campo numérico contém Label, DecrementTrigger, Input, IncrementTrigger, Description e ErrorMessage. A descrição visível explica tanto o intervalo permitido quanto o efeito da alteração das porções. Os disparadores têm rótulos claros — Menos porções e Mais porções —, para que os sinais de menos e mais não precisem, sozinhos, transmitir todo o nome acessível.

validationState alterna entre válido e inválido conforme o predicado da aplicação. ErrorMessage pertence a esse campo nativo, e não a um parágrafo sem relação inserido em outro lugar da página. Em um formulário enviado, Kobalte também oferece um HiddenInput para serializar o valor bruto; este exemplo local não o inclui porque não tem um endpoint de envio nem um payload de formulário.

Consulte a especificação de apoio

Sistema de design Kobalte: mantenha a seleção de ingredientes em um Set imutável

Mantenha a seleção de ingredientes em um conjunto imutável

Cada ingrediente usa a estrutura completa de Checkbox: Input, Control, Indicator e Label. O input real preserva a semântica e a interação pelo teclado, enquanto o controle visível recebe a marca de seleção e os estilos originais dos estados. O rótulo inclui tanto o nome do ingrediente quanto a quantidade, tornando-a parte do item que a pessoa está reunindo.

mark cria um novo Set a partir da seleção anterior antes de adicionar ou remover um ID. Alterar apenas o Set existente não produziria uma atualização do sinal igualmente confiável. A contagem exibida lê o tamanho do Set, e cada Checkbox verifica a seleção por ID estável do ingrediente. Uma alteração na quantidade limpa toda a seleção, pois reunir 320 gramas não comprova que o novo requisito de 400 gramas foi atendido.

  • Associe cada nome de ingrediente visível a Checkbox.Label.
  • Mantenha Checkbox.Input mesmo quando o quadrado visível tiver um estilo personalizado.
  • Use IDs estáveis para a seleção e crie um novo Set a cada atualização.

Consulte a especificação de apoio

Sistema de design Kobalte: use um botão de alternância para uma escolha local persistente

Use um botão de alternância para uma escolha local persistente

A fixação é controlada por um ToggleButton com pressed e onChange, não por um botão comum que apenas altera a legenda. O estado pressed é refletido na semântica nativa e no seletor data-pressed. O rótulo muda para Fixada com uma marca de seleção, e o status próximo explica que isso afeta apenas a prévia local.

Fixar a receita não altera as quantidades nem a seleção dos ingredientes. Alterar as porções limpa as seleções de ingredientes, mas mantém a fixação, pois a identidade da receita não mudou. Restaurar a receita padrão redefine juntos os três aspectos do modelo: quatro porções, um Set de ingredientes vazio e a fixação desativada. Esses limites diferentes de redefinição refletem o significado de cada estado, em vez de limpar tudo a cada interação.

Consulte a especificação de apoio

Sistema de design Kobalte: defina uma configuração visual reproduzível para controles sem estilo

Defina uma configuração visual reproduzível para controles headless

A folha de estilos da receita separa a superfície de edição do campo numérico, os disparadores de incremento e decremento e a função de seleção das caixas de seleção. O texto atenuado dos ingredientes só é aplicado depois da seleção; as quantidades permanecem legíveis para que a pessoa ainda possa comparar o que reuniu com a quantidade atual de porções.

O espaçamento usa quatro, oito, doze, dezesseis, vinte e quatro e trinta e dois pixels. A hierarquia tipográfica original combina uma fonte sem serifa do sistema para os controles com Georgia no título da receita. Uma caixa de seleção visual de vinte e um pixels aparece ao lado de um rótulo associado completo; o rótulo continua sendo a área de ativação mais prática. Esses valores exatos estão incluídos no CSS completo para que seja possível reproduzir o exemplo sem precisar deduzi-los de uma captura de tela.

Consulte a especificação de apoio

Sistema de design Kobalte: permita que a composição se ajuste sem ocultar informações

Permita que a composição se ajuste sem ocultar informações

O layout amplo combina um cartão de receita flexível com um painel de porções de 280 pixels. Em 760 pixels, os elementos passam para uma única coluna. O título do cartão pode ocupar mais de uma linha, os rótulos dos ingredientes podem quebrar linha e a quantidade permanece compacta, sem quebra. No menor ponto de interrupção, o espaçamento interno do painel e o tamanho da fonte dos rótulos diminuem ligeiramente, em vez de ocultar as quantidades.

A ilustração da salada é original CSS e está marcada como decorativa. O nome da receita, a quantidade de porções e as quantidades dos ingredientes transmitem o significado independentemente da imagem. Ao alterar a ilustração ou estreitar o cartão, mantenha os rótulos completos dos ingredientes e suas quantidades; são eles que orientam a tarefa de reunir os itens.

Consulte a especificação de apoio

Sistema de design Kobalte: trate alterações nas quantidades como atualizações de estado dependente

Trate as alterações de quantidade como uma atualização de estado dependente

Cada porção corresponde a 250 gramas distribuídas entre os cinco ingredientes. Ao passar de quatro para cinco porções, o total exibido muda de 1,000 para 1,250 gramas, e a quantidade de laranja necessária passa de 320 para 400 gramas. As seleções já feitas são limpas porque se referiam às quantidades anteriores, enquanto a fixação da receita permanece inalterada, pois a identidade da receita é a mesma.

A função de atualização primeiro compara o valor recebido com o sinal atual usando Object.is. Uma chamada sem alteração não deve limpar a lista de ingredientes nem anunciar uma mudança na quantidade. Valores inválidos ocultam as quantidades e desativam a seleção dos ingredientes; a mensagem de erro explica então o intervalo aceito. Essas regras permitem observar edições intermediárias sem deixar que um número inválido produza um total enganoso.

  1. Selecione Gomos de laranja com quatro porções.
  2. Aumente para cinco e confirme 400 g de laranja, 1,250 g no total e zero itens reunidos.
  3. Fixe a receita, restaure os padrões e confirme que todos os estados locais foram redefinidos.

Consulte a especificação de apoio

Sistema de design Kobalte: use uma compilação Solid e inclua a folha de estilos completa

Use uma compilação do Solid e inclua a folha de estilos completa

A configuração usa o template Vite para Solid, fixa as versões verificadas do Solid e de Kobalte e monta a aplicação em uma raiz explícita. O componente e a folha de estilos apresentados acima são arquivos completos. Este exemplo não exige provedor de tema, pacote de ícones, fonte externa nem backend.

Ao integrar a outro aplicativo, mantenha alinhados o compilador do framework e a geração de componentes. Uma transformação de React não converterá JSX do Solid em um aplicativo Kobalte correto. Da mesma forma, antes de substituir a dependência fixada por uma futura versão principal de Kobalte API, consulte a documentação correspondente. As quantidades da receita e a paleta são dados de exemplo da aplicação; adaptá-los não exige alterar o contrato das primitivas nativas.

Consulte a especificação de apoio

Exemplos de componentes do sistema de design Kobalte

Componente de receita reativo completo

Campo numérico nativo, estrutura de Checkbox e botão de alternância com comportamentos explícitos para valores inválidos e redefinição.

jsx
import {createSignal,createMemo,For} from 'solid-js';
import {NumberField} from '@kobalte/core/number-field';
import {Checkbox} from '@kobalte/core/checkbox';
import {ToggleButton} from '@kobalte/core/toggle-button';
export default function App(){
 const[portions,setPortions]=createSignal(4),[checked,setChecked]=createSignal(new Set()),[pinned,setPinned]=createSignal(false),[notice,setNotice]=createSignal('');
 const ingredients=[{id:'orange',name:'Orange segments',grams:80},{id:'beans',name:'Cooked white beans',grams:90},{id:'leaves',name:'Salad leaves',grams:25},{id:'fennel',name:'Thinly sliced fennel',grams:40},{id:'dressing',name:'Citrus dressing',grams:15}];
 const valid=createMemo(()=>Number.isFinite(portions())&&Number.isInteger(portions())&&portions()>=1&&portions()<=8);
 function update(value){if(Object.is(value,portions()))return;setPortions(value);setChecked(new Set());setNotice('Portions changed. Ingredient checks were cleared for the new quantities.')}
 function mark(id,value){setChecked(previous=>{const next=new Set(previous);value?next.add(id):next.delete(id);return next})}
 function reset(){setPortions(4);setChecked(new Set());setPinned(false);setNotice('Restored four portions and an unchecked ingredient list.')}
 return <main class="recipe-shell"><header><p class="eyebrow">KITCHEN NOTEBOOK · REACTIVE RECIPE CARD</p><h1>A brighter bowl, sized for your table.</h1><p>Change the portions, check the scaled ingredients and pin the recipe locally. The quantities are original sample data for this interface.</p></header><div class="recipe-layout"><section class="recipe-card"><div class="salad-art" aria-hidden="true"><div class="plate"><i/><b/><em/><span/><strong/></div></div><div class="recipe-heading"><div><p class="eyebrow">CITRUS & WHITE BEAN SALAD</p><h2>Fresh, crisp, generous.</h2></div><ToggleButton class="pin" pressed={pinned()} onChange={value=>{setPinned(value);setNotice(value?'Recipe pinned in this local preview.':'Recipe unpinned in this local preview.')}} aria-label="Pin recipe">{pinned()?'Pinned ✓':'Pin recipe'}</ToggleButton></div><p class="recipe-description">Sweet citrus, tender beans and crisp fennel. Use the checklist to gather the amounts for your chosen portion count.</p><div class="ingredient-title"><h3>Ingredients</h3><span>{checked().size} / 5 gathered</span></div><div class="ingredients"><For each={ingredients}>{ingredient=><Checkbox class="ingredient" checked={checked().has(ingredient.id)} onChange={value=>mark(ingredient.id,value)} disabled={!valid()}><Checkbox.Input/><Checkbox.Control class="check-control"><Checkbox.Indicator>✓</Checkbox.Indicator></Checkbox.Control><Checkbox.Label class="ingredient-label"><span>{ingredient.name}</span><strong>{valid()?ingredient.grams*portions()+' g':'—'}</strong></Checkbox.Label></Checkbox>}</For></div></section><aside class="portion-panel"><h2>Make it yours</h2><NumberField rawValue={portions()} onRawValueChange={update} minValue={1} maxValue={8} step={1} formatOptions={{maximumFractionDigits:0}} validationState={valid()?'valid':'invalid'} class="portion-field"><NumberField.Label class="field-label">Portions</NumberField.Label><div class="stepper"><NumberField.DecrementTrigger aria-label="Fewer portions">−</NumberField.DecrementTrigger><NumberField.Input/><NumberField.IncrementTrigger aria-label="More portions">+</NumberField.IncrementTrigger></div><NumberField.Description class="field-description">Choose 1–8 whole portions. Changing the quantity clears ingredient checks.</NumberField.Description><NumberField.ErrorMessage class="field-error">Enter a whole number from 1 to 8.</NumberField.ErrorMessage></NumberField><div class="portion-total"><span>Total ingredients</span><strong>{valid()?ingredients.reduce((s,x)=>s+x.grams,0)*portions()+' g':'—'}</strong></div><h3>A simple preparation order</h3><ol><li>Gather the scaled amounts and drain the cooked beans.</li><li>Combine the orange, fennel and leaves in a bowl.</li><li>Add the beans and dressing, then toss gently.</li></ol><button class="reset" onClick={reset}>Restore default recipe</button><p role="status" class="notice">{notice()||(pinned()?'Recipe pinned in this local preview.':'No shopping order or account update is created.')}</p></aside></div></main>
}

Referência de código

  • Use o template Vite para Solid, não um compilador JSX de React.
  • rawValue contém o estado numérico; todas as quantidades dos ingredientes são derivadas desse sinal.
  • Alterar as porções limpa o Set de seleções, pois as quantidades anteriores não são mais atuais.

Configuração visual original completa

Propriedades personalizadas semânticas, hierarquia tipográfica, espaçamento, estilos de estado e ilustração original CSS.

css
:root{--recipe-canvas:#f6f2ec;--recipe-surface:#fffefa;--recipe-ink:#403a30;--recipe-muted:#807465;--recipe-line:#e6ded2;--recipe-action:#95612f;--recipe-on-action:#fff;--recipe-focus:#b88955;--recipe-soft:#f1e4d3;--recipe-space-1:4px;--recipe-space-2:8px;--recipe-space-3:12px;--recipe-space-4:16px;--recipe-space-6:24px;--recipe-space-8:32px}html,body,#app{height:auto;min-height:0;margin:0}*{box-sizing:border-box}body{font-family:system-ui,sans-serif;color:var(--recipe-ink);background:var(--recipe-canvas)}button,input{font:inherit}button{cursor:pointer}button:disabled{opacity:.4;cursor:default}button:focus-visible,input:focus-visible{outline:3px solid var(--recipe-focus);outline-offset:3px}.recipe-shell{max-width:1090px;margin:auto;padding:32px}.eyebrow{font-size:10px;font-weight:700;letter-spacing:.13em;color:var(--recipe-muted)}h1{font-size:34px;line-height:1.15;max-width:700px;margin:12px 0}header>p:last-child{font-size:14px;line-height:1.7;color:var(--recipe-muted);max-width:630px}.recipe-layout{display:grid;grid-template-columns:minmax(0,1fr) 280px;gap:24px;margin-top:28px}.recipe-card,.portion-panel{background:var(--recipe-surface);border:1px solid var(--recipe-line);border-radius:16px;padding:24px;min-width:0}.portion-panel{align-self:start}.salad-art{height:180px;background:#e8e4d5;border-radius:10px;position:relative;overflow:hidden;margin-bottom:24px}.plate{position:absolute;width:210px;height:210px;left:50%;top:18px;transform:translateX(-50%);border:18px solid #faf8f0;border-radius:50%;background:#dedcbf;box-shadow:0 10px 25px #5e584b1c}.plate i,.plate b,.plate em,.plate span,.plate strong{position:absolute;display:block}.plate i{width:85px;height:42px;border-radius:0 0 85px 85px;background:#dc9658;border:6px solid #f7c888;left:14px;top:18px;transform:rotate(-23deg)}.plate b{width:72px;height:36px;border-radius:0 0 72px 72px;background:#dc9658;border:6px solid #f7c888;left:75px;top:83px;transform:rotate(28deg)}.plate em{width:50px;height:77px;background:#83976a;border-radius:80% 0 80% 0;left:108px;top:4px;transform:rotate(20deg)}.plate span{width:30px;height:50px;background:#667f56;border-radius:80% 0 80% 0;left:25px;top:88px;transform:rotate(-23deg)}.plate strong{width:24px;height:34px;background:#fff3d7;border-radius:50%;left:80px;top:46px;box-shadow:-34px 14px 0 #fff3d7,25px 28px 0 #fff3d7}.recipe-heading{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap}.recipe-heading h2{font:400 30px/1.2 Georgia,serif;margin:8px 0}.pin{border:1px solid var(--recipe-line);background:white;border-radius:999px;padding:9px 13px;font-size:11px;color:var(--recipe-action)}.pin[data-pressed]{background:var(--recipe-soft);border-color:var(--recipe-action)}.recipe-description{font-size:13px;line-height:1.7;color:var(--recipe-muted)}.ingredient-title{display:flex;justify-content:space-between;align-items:center;gap:12px;margin-top:24px}.ingredient-title h3{font-size:16px;margin:0}.ingredient-title span{font-size:11px;color:var(--recipe-muted)}.ingredients{margin-top:14px}.ingredient{display:flex;gap:12px;align-items:center;border-top:1px solid var(--recipe-line);padding:16px 0;cursor:pointer}.check-control{height:21px;width:21px;border:1px solid #b9ab98;border-radius:5px;flex-shrink:0;display:flex;justify-content:center;align-items:center;font-size:13px}.check-control[data-checked]{background:var(--recipe-action);color:white;border-color:var(--recipe-action)}.check-control[data-focus-visible]{outline:3px solid var(--recipe-focus);outline-offset:3px}.ingredient-label{display:flex;justify-content:space-between;gap:12px;width:100%;font-size:13px;line-height:1.5;cursor:pointer}.ingredient-label strong{white-space:nowrap;font-size:12px}.ingredient[data-checked] .ingredient-label>span{text-decoration:line-through;color:var(--recipe-muted)}.portion-panel>h2{font-size:20px;margin:0 0 24px}.field-label{font-size:13px;font-weight:600}.stepper{display:flex;margin-top:10px;border:1px solid var(--recipe-line);border-radius:8px;overflow:hidden}.stepper button{width:42px;flex-shrink:0;background:var(--recipe-soft);border:0;font-size:21px}.stepper input{width:100%;min-width:0;text-align:center;border:0;padding:12px 0;background:white;color:var(--recipe-ink)}.field-description{font-size:11px;line-height:1.7;color:var(--recipe-muted);margin:10px 0 0}.field-error{font-size:12px;color:#a73324}.portion-total{display:flex;justify-content:space-between;gap:12px;margin:24px 0;padding:18px 0;border-top:1px solid var(--recipe-line);border-bottom:1px solid var(--recipe-line);font-size:12px}.portion-total strong{font-size:19px;white-space:nowrap}.portion-panel h3{font-size:13px;line-height:1.6}.portion-panel ol{padding-left:18px;font-size:12px;line-height:1.7;color:var(--recipe-muted)}.portion-panel li{margin-bottom:12px}.reset{width:100%;padding:12px;border:1px solid var(--recipe-line);border-radius:8px;background:white;color:var(--recipe-action);font-size:12px;margin-top:16px}.notice{font-size:11px;line-height:1.7;color:var(--recipe-muted)}@media(max-width:760px){.recipe-layout{grid-template-columns:minmax(0,1fr)}.recipe-shell{padding:20px}h1{font-size:29px}}@media(max-width:360px){.recipe-shell{padding:14px}.recipe-card,.portion-panel{padding:16px}.ingredient-label{gap:8px;font-size:12px}.ingredient{gap:8px}.ingredient-label strong{font-size:11px}.recipe-heading h2{font-size:27px}}

Referência de código

  • Esses valores recipe-* são escolhas originais da aplicação, não tokens oficiais de Kobalte.
  • A folha de estilos completa inclui uma composição responsiva e foco visível.
04

Configuração de agente do sistema de design Kobalte

Ponto de partida para implementação

O template Vite para Solid fornece o compilador JSX necessário para Solid. Os exemplos completos de App.jsx e style.css acima contêm todo o código e os estilos da aplicação; não é necessário nenhum provedor nem serviço externo.

javascript
// npm create vite@latest kobalte-recipe -- --template solid
// cd kobalte-recipe && npm install
// npm install [email protected] @kobalte/[email protected]
// Save the complete examples as src/App.jsx and src/style.css.
// src/index.jsx:
import {render} from 'solid-js/web';
import App from './App.jsx';
import './style.css';
render(() => <App />, document.getElementById('app'));
// index.html: <div id="app"></div><script type="module" src="/src/index.jsx"></script>
// npm run dev

Um prompt para seu projeto

Prompt para agente de IA
Use Kobalte 0.13.14 com Solid 1.9.15 para reproduzir o cartão de receita reativo. Mantenha NumberField rawValue/onRawValueChange, limites de um a oito, validação de porções inteiras e toda a estrutura de Label/Description/ErrorMessage. Calcule os gramas dos ingredientes e os totais com base no sinal de porções; uma alteração real na quantidade limpa o Set imutável de IDs marcados, mas preserva a fixação da receita. Mantenha as partes nativas de Checkbox e ToggleButton pressed/onChange, desative a coleta quando a quantidade for inválida e restaure quatro porções, nenhuma marcação e nenhuma fixação ao redefinir. Inclua a receita original completa CSS; todo o estado permanece local.
05

Fontes e escopo do sistema de design Kobalte

Verificado com Kobalte 0.13.14 e Solid 1.9.15. Todos os tokens recipe-*, a ilustração CSS e as quantidades dos ingredientes são dados originais do exemplo. A fixação e a coleta permanecem no estado local; não há conexão com conta, serviço de compras ou armazenamento persistente de receitas.