# Sistema de design Microsoft Fluent 2

## Prévia da interface do sistema de design Microsoft Fluent 2

Alterne entre Resumo e Pessoas, mude o tema para a Web e teste adições inválidas, duplicadas e bem-sucedidas em ambiente local. Nenhum convite será enviado.

## Referência de design do sistema de design Microsoft Fluent 2

Referências para a Web do Fluent UI React v9. Os valores numéricos globais vêm das fontes de tokens React vinculadas. Os valores de cor abaixo correspondem ao mapeamento de alias claro com brandWeb, não aos comentários hexadecimais desatualizados de exemplo em alias/lightColor.ts. As cores escuras e os temas do Teams têm mapeamentos próprios. A tabela de tipografia do site lista atualmente Subtitle1 como 20/26; a fonte React fonts.ts usa 20/28, valor adotado nesta implementação. Fontes consultadas em 3 de outubro de 2026; confira a versão registrada no arquivo de lock.

### Funções neutras do tema claro

| Nome / Regra | Valor / Configuração | Uso |
| --- | --- | --- |
| colorNeutralForeground1 | #242424 | Texto principal do documento. |
| colorNeutralForeground2 | #424242 | Rótulos secundários. |
| colorNeutralForeground3 | #616161 | Metadados; ainda é necessário verificar o contraste final. |
| colorNeutralBackground1 | #ffffff | Superfície principal. |
| colorNeutralBackground2 | #fafafa | Superfície de apoio discreta. |
| colorNeutralBackground3 | #f5f5f5 | Plano de fundo do espaço de trabalho. |
| colorNeutralStroke1 | #d1d1d1 | Limite comum de componente. |
| colorNeutralStroke2 | #e0e0e0 | Divisor sutil. |
| colorNeutralStrokeAccessible | #616161 | Limite de controle com contraste mais alto. |
| colorNeutralForegroundOnBrand | #ffffff | Texto sobre fundos de marca compatíveis. |
| colorStrokeFocus1 / colorStrokeFocus2 | #ffffff / #000000 | Duas cores de foco; preserve o contraste na borda do componente. |
### Funções de marca para a Web, resolvidas a partir de brandWeb

| Nome / Regra | Valor / Configuração | Uso |
| --- | --- | --- |
| colorBrandBackground | #0f6cbd · brandWeb[80] | Ação primária em webLightTheme. |
| colorBrandBackgroundHover | #115ea3 · brandWeb[70] | Variação de hover dessa ação. |
| colorBrandBackgroundPressed | #0c3b5e · brandWeb[40] | Variação pressionada; não representa um status selecionado genérico. |
| colorBrandBackground2 | #ebf3fc · brandWeb[160] | Fundo sutil de marca. |
| colorBrandForegroundLink | #115ea3 · brandWeb[70] | Cor de primeiro plano do link no tema claro para a Web. |
### Espaçamento horizontal e vertical

| Nome / Regra | Valor / Configuração | Uso |
| --- | --- | --- |
| spacingHorizontalNone / spacingVerticalNone | 0 | Escolha o eixo conforme a direção do layout; intervalo de espaçamento compartilhado. |
| spacingHorizontalXXS / spacingVerticalXXS | 2px | Escolha o eixo conforme a direção do layout; intervalo de espaçamento compartilhado. |
| spacingHorizontalXS / spacingVerticalXS | 4px | Escolha o eixo conforme a direção do layout; intervalo de espaçamento compartilhado. |
| spacingHorizontalSNudge / spacingVerticalSNudge | 6px | Escolha o eixo conforme a direção do layout; ajuste de alinhamento do ícone. |
| spacingHorizontalS / spacingVerticalS | 8px | Escolha o eixo conforme a direção do layout; intervalo de espaçamento compartilhado. |
| spacingHorizontalMNudge / spacingVerticalMNudge | 10px | Escolha o eixo conforme a direção do layout; ajuste de alinhamento do ícone. |
| spacingHorizontalM / spacingVerticalM | 12px | Escolha o eixo conforme a direção do layout; intervalo de espaçamento compartilhado. |
| spacingHorizontalL / spacingVerticalL | 16px | Escolha o eixo conforme a direção do layout; intervalo de espaçamento compartilhado. |
| spacingHorizontalXL / spacingVerticalXL | 20px | Escolha o eixo conforme a direção do layout; intervalo de espaçamento compartilhado. |
| spacingHorizontalXXL / spacingVerticalXXL | 24px | Escolha o eixo conforme a direção do layout; intervalo de espaçamento compartilhado. |
| spacingHorizontalXXXL / spacingVerticalXXXL | 32px | Escolha o eixo conforme a direção do layout; intervalo de espaçamento compartilhado. |
### Combinações tipográficas para a Web de React

| Nome / Regra | Valor / Configuração | Uso |
| --- | --- | --- |
| Caption2 · fontSizeBase100 | 10px / 14px / 400 | Tamanho / altura da linha / peso. Combine com lineHeightBase100. |
| Caption1 · fontSizeBase200 | 12px / 16px / 400 | Tamanho / altura da linha / peso. Combine com lineHeightBase200. |
| Body1 · fontSizeBase300 | 14px / 20px / 400 | Tamanho / altura da linha / peso. Combine com lineHeightBase300. |
| Body2 / Subtitle2 · fontSizeBase400 | 16px / 22px / 400 / 600 | Tamanho / altura da linha / peso. Combine com lineHeightBase400. |
| Subtitle1 · fontSizeBase500 | 20px / 28px / 600 | Tamanho / altura da linha / peso. Combine com lineHeightBase500. |
| Title3 · fontSizeBase600 | 24px / 32px / 600 | Tamanho / altura da linha / peso. Combine com lineHeightBase600. |
| Title2 · fontSizeHero700 | 28px / 36px / 600 | Tamanho / altura da linha / peso. Combine com lineHeightHero700. |
| Title1 · fontSizeHero800 | 32px / 40px / 600 | Tamanho / altura da linha / peso. Combine com lineHeightHero800. |
| LargeTitle · fontSizeHero900 | 40px / 52px / 600 | Tamanho / altura da linha / peso. Combine com lineHeightHero900. |
| Display · fontSizeHero1000 | 68px / 92px / 600 | Tamanho / altura da linha / peso. Combine com lineHeightHero1000. |
### Família tipográfica e peso

| Nome / Regra | Valor / Configuração | Uso |
| --- | --- | --- |
| fontFamilyBase | 'Segoe UI', 'Segoe UI Web (West European)', -apple-system, BlinkMacSystemFont, Roboto, 'Helvetica Neue', sans-serif | Pilha padrão do provedor para a Web; não identifique a fonte do restante do site como Fluent. |
| fontFamilyMonospace | Consolas, 'Courier New', Courier, monospace | Código e identificadores técnicos. |
| fontWeightRegular / Medium / Semibold / Bold | 400 / 500 / 600 / 700 | Use o peso da função com a escala tipográfica correspondente. |
### Tokens de formato

| Nome / Regra | Valor / Configuração | Uso |
| --- | --- | --- |
| borderRadiusNone | 0 | Use primeiro o padrão do componente; este é um token disponível, não uma exigência para todas as superfícies. |
| borderRadiusSmall | 2px | Use primeiro o padrão do componente; este é um token disponível, não uma exigência para todas as superfícies. |
| borderRadiusMedium | 4px | Use primeiro o padrão do componente; este é um token disponível, não uma exigência para todas as superfícies. |
| borderRadiusLarge | 6px | Use primeiro o padrão do componente; este é um token disponível, não uma exigência para todas as superfícies. |
| borderRadiusXLarge | 8px | Use primeiro o padrão do componente; este é um token disponível, não uma exigência para todas as superfícies. |
| borderRadius2XLarge | 12px | Use primeiro o padrão do componente; este é um token disponível, não uma exigência para todas as superfícies. |
| borderRadius3XLarge | 16px | Use primeiro o padrão do componente; este é um token disponível, não uma exigência para todas as superfícies. |
| borderRadius4XLarge | 24px | Use primeiro o padrão do componente; este é um token disponível, não uma exigência para todas as superfícies. |
| borderRadius5XLarge | 32px | Use primeiro o padrão do componente; este é um token disponível, não uma exigência para todas as superfícies. |
| borderRadius6XLarge | 40px | Use primeiro o padrão do componente; este é um token disponível, não uma exigência para todas as superfícies. |
| borderRadiusCircular | 10000px | Use primeiro o padrão do componente; este é um token disponível, não uma exigência para todas as superfícies. |
### Escala de duração de movimento

| Nome / Regra | Valor / Configuração | Uso |
| --- | --- | --- |
| durationUltraFast | 50ms | Token disponível em tempo de execução; o exemplo usa Fast no hover e remove as transições quando a opção de movimento reduzido está ativada. |
| durationFaster | 100ms | Token disponível em tempo de execução; o exemplo usa Fast no hover e remove as transições quando a opção de movimento reduzido está ativada. |
| durationFast | 150ms | Token disponível em tempo de execução; o exemplo usa Fast no hover e remove as transições quando a opção de movimento reduzido está ativada. |
| durationNormal | 200ms | Token disponível em tempo de execução; o exemplo usa Fast no hover e remove as transições quando a opção de movimento reduzido está ativada. |
| durationGentle | 250ms | Token disponível em tempo de execução; o exemplo usa Fast no hover e remove as transições quando a opção de movimento reduzido está ativada. |
| durationSlow | 300ms | Token disponível em tempo de execução; o exemplo usa Fast no hover e remove as transições quando a opção de movimento reduzido está ativada. |
| durationSlower | 400ms | Token disponível em tempo de execução; o exemplo usa Fast no hover e remove as transições quando a opção de movimento reduzido está ativada. |
| durationUltraSlow | 500ms | Token disponível em tempo de execução; o exemplo usa Fast no hover e remove as transições quando a opção de movimento reduzido está ativada. |
### A mudança de tema é semântica, não uma inversão

| Nome / Regra | Valor / Configuração | Uso |
| --- | --- | --- |
| colorNeutralBackground1 | #292929 | Superfície principal escura em vez de #ffffff claro. |
| colorNeutralForeground1 | #ffffff | Texto principal escuro em vez de #242424 claro. |
| colorNeutralForeground2 | #d6d6d6 | Texto secundário escuro. |
| colorBrandBackground | brandWeb[70] = #115ea3 | Preenchimento primário escuro; no modo claro, usa brandWeb[80]. |
| colorBrandForegroundLink | brandWeb[100] = #479ef5 | Função de link escura; diferente do preenchimento do botão primário. |
## Guia completo do sistema de design Microsoft Fluent 2

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

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

## Sistema de design Microsoft Fluent 2 e implementação React v9

### Escolha explicitamente a implementação para a Web

> Fluent 2 é uma linguagem de design multiplataforma. Este guia adota uma opção mais específica e prática: @fluentui/react-components, a biblioteca convergida React v9. A raiz é FluentProvider, e este exemplo usa webLightTheme ou webDarkTheme. Um tema do Teams é uma configuração de marca separada; o roxo familiar do Teams não é a escala azul padrão para a Web. Os recursos nativos para Windows, iOS e Android também não são pacotes React intercambiáveis.

O exemplo completo abaixo exibe um resumo do projeto e uma lista de acesso local. Ele combina Title3 e Body1 para criar hierarquia, TabList e Tab para duas visualizações, e Dialog com Field, Input e Select para uma decisão de acesso. O código não afirma enviar um convite real. Essa distinção permite demonstrar o comportamento dos componentes sem inventar um serviço de compartilhamento.

Consulte a especificação de apoio

### O que o provedor realmente oferece

FluentProvider aplica variáveis de tema à sua subárvore. Uma superfície personalizada estilizada com tokens.colorNeutralBackground2 acompanha o tema selecionado, enquanto uma superfície com branco definido diretamente não acompanha. Posicione o provider acima dos componentes que precisam do tema. A biblioteca estiliza seus componentes por meio do runtime de estilização; o exemplo não pede que você importe um arquivo fluent.css inexistente.

Consulte a especificação de apoio

## Sistema de design Microsoft Fluent 2: cores semânticas e a escala real da marca para web

### Resolva os aliases usando a marca escolhida

A origem do alias claro atribui colorBrandBackground a brand[80]. O tema web fornece brandWeb, em que a etapa 80 é #0f6cbd. Portanto, #0f6cbd é o preenchimento primário resultante neste exemplo. O arquivo de aliases contém comentários ilustrativos antigos com valores hexadecimais; interpretá-los como a paleta web atual levaria ao resultado errado. A relação entre as origens, não o comentário, determina o valor.

Use colorNeutralForeground1 para o texto principal, colorNeutralForeground2 para rótulos de apoio e colorNeutralBackground3 para a área de trabalho ao redor. Uma ação primária pode usar colorBrandBackground com colorNeutralForegroundOnBrand. Os nomes das funções continuam sendo a referência quando o tema muda. A permissão de uma pessoa é representada por texto; uma borda azul ou um indicador azul na aba expressa seleção, não autorização.

Consulte a especificação de apoio

### O modo escuro altera o mapeamento

No modo escuro, a superfície principal é resolvida para #292929 e o texto primário, para #ffffff. O preenchimento da marca primária usa a etapa 70, enquanto o texto de link usa a etapa 100. Por isso, a visualização altera em conjunto as funções dos tokens, em vez de apenas inverter o fundo da página. Depois de alternar o tema, compare a caixa de diálogo de acesso, a aba selecionada e os metadados atenuados: todos fazem parte do mesmo limite de tema.

Consulte a especificação de apoio

## Sistema de design Microsoft Fluent 2: tipografia — associe as funções de texto aos tokens do runtime

### Use pares de tamanho e altura de linha

Body1 é o estilo básico de leitura, com 14px e altura de linha de 20px. Caption1 usa 12/16, Subtitle2 usa 16/22 com peso semibold e Title3 usa 24/32 com peso semibold. Esses pares estabelecem uma hierarquia clara entre títulos, rótulos e texto corrido, sem tamanhos intermediários arbitrários. A tabela acima inclui etapas maiores para referência, mas uma área de trabalho compacta não precisa de um título em tamanho de destaque.

Atualmente, a origem dos tokens de React e o site de design divergem em um detalhe útil: fontSizeBase500 / lineHeightBase500 é 20/28 na origem do runtime, enquanto o gráfico de design na web lista Subtitle1 como 20/26. Este exemplo segue o par do runtime. Trate diferenças desse tipo como uma verificação de versão e implementação, em vez de combinar silenciosamente os valores que melhor se encaixem no mockup.

Consulte a especificação de apoio

### Predefinições de texto e pilha de fontes

Use a semântica de HTML junto com a predefinição visual: Title3 as="h1" é um título; aplicar estilo, por si só, não cria a hierarquia do documento. A pilha básica começa com Segoe UI e inclui fontes alternativas da plataforma. Não inclua uma fonte proprietária só para fazer uma captura de tela corresponder ao exemplo. O espécime usa a pilha documentada, portanto os glifos exatos dependem das fontes instaladas no dispositivo de visualização.

Para texto personalizado, typographyStyles.body1 reúne família, tamanho, peso e altura de linha. Prefira essa combinação a copiar apenas fontSizeBase300. A lista de acessos usa endereços de e-mail legíveis, com quebra de linha; o rótulo da permissão usa destaque semibold, mas não compete com o título da página.

Consulte a especificação de apoio

## Sistema de design Microsoft Fluent 2: espaçamento, forma e densidade em uma área de trabalho

### Os nomes de espaçamento de React não são os nomes usados na ferramenta de design

As orientações gerais de layout do Fluent usam nomes globais de tamanho e um ritmo de quatro unidades, incluindo 2, 6 e 10 para ajustes de alinhamento. React expõe aliases específicos por eixo: spacingHorizontalS é 8px, M é 12px, L é 16px e XXL é 24px, com nomes verticais correspondentes. Não transforme size120 em spacingHorizontal120; esse identificador de React não existe.

Esta composição usa preenchimento de 24px nos painéis, espaço de 16px entre os campos do formulário e espaçamento de 12px entre as linhas de acesso. A largura exata da página e a disposição em duas colunas são decisões da aplicação, não pontos de interrupção oficiais do Fluent. Em telas estreitas, o contexto de apoio da equipe passa para baixo do texto principal, enquanto os dois rótulos das abas continuam visíveis.

Consulte a especificação de apoio

### A forma descreve a escala dos componentes

Os tokens de raio disponíveis incluem Medium, de 4px, e XLarge, de 8px, mas isso não significa que todos os controles devam ter o mesmo raio. O espécime de HTML usa uma forma de controle de 4px e uma superfície de documento de 8px como escolhas explícitas para estudo. Os componentes nativos devem manter sua anatomia padrão compatível, a menos que o requisito de design selecionado justifique uma substituição. Não acesse nomes de classes gerados para forçar outra forma.

Consulte a especificação de apoio

## Sistema de design Microsoft Fluent 2 TabList: seleção, painéis e comportamento do teclado

### O valor selecionado é um estado da aplicação

O exemplo mantém a aba em estado de React e o passa para selectedValue. onTabSelect recebe data.value, que atualiza esse estado. Cada Tab tem um valor estável e aponta para seu painel por meio de aria-controls. Cada painel tem role="tabpanel", aria-labelledby e uma condição hidden. O componente não fornece o texto principal do projeto nem a lista de acessos; esses elementos são renderizados explicitamente.

API separa o foco da seleção: selectTabOnFocus tem false como padrão. No espécime independente, as teclas Left/Right e Home/End movem o foco, e Enter ou Space ativa a aba em foco. Esse comportamento de ativação manual permite examinar as opções de abas sem provocar uma mudança inesperada de conteúdo. Mantenha os rótulos das abas curtos; as orientações oficiais de layout indicam que abas horizontais não quebram linha nem rolam automaticamente.

Consulte a especificação de apoio

### Escolha outro padrão quando as categorias aumentarem

Brief e People são duas visualizações estreitamente relacionadas de uma mesma área de trabalho, portanto as abas são adequadas. Uma lista com todos os projetos da organização seria navegação, não outra linha de abas. Se as categorias deixarem de caber em uma tela estreita, planeje deliberadamente um padrão de transbordamento ou outra forma de seleção; reduzir os rótulos até que fiquem ilegíveis não é uma estratégia responsiva.

Consulte a especificação de apoio

## Sistema de design Microsoft Fluent 2 Field: rótulos acessíveis e validação significativa

### Um campo é uma composição, não apenas um input estilizado

Field combina um espaço para rótulo, o controle filho, uma dica opcional e uma mensagem de validação. O contrato documentado do controle inclui id, aria-labelledby, aria-describedby, aria-invalid e aria-required. Inputs do Fluent podem ser filhos diretos; controles personalizados podem consumir as propriedades da função de renderização. Por isso, envolver o Input de e-mail em Field é mais útil do que colocar um texto sem relação acima do input.

No exemplo, validationState só passa a error depois que uma tentativa local inválida é enviada. A aplicação verifica se o valor tem formato de e-mail e se há duplicatas na lista em memória. O valor digitado é preservado, e uma orientação de correção é exibida por meio de validationMessage. Field apresenta e associa esse resultado; não verifica se o endereço existe, não envia um convite nem concede acesso.

Consulte a especificação de apoio

### Mantenha o estado de validação distinto da permissão

O Select de permissão contém Can view e Can edit, enquanto o campo de e-mail tem seu próprio estado de erro. Evite deixar toda a caixa de diálogo vermelha porque um endereço está incompleto. required é uma indicação de acessibilidade, não um substituto para a autorização ou validação no servidor. O formulário de exemplo usa noValidate para demonstrar a mensagem de validação explícita de maneira consistente; em produção, o serviço também precisa validar a solicitação.

- Endereço vazio ou malformado: mantenha a caixa de diálogo aberta e explique o formato aceito.

- Endereço já presente na lista de acessos local: mostre uma mensagem específica de duplicidade sem adicionar outra linha.

- Novo endereço válido: adicione localmente a permissão escolhida, feche a caixa de diálogo e informe com clareza que o resultado é apenas local.

Consulte a especificação de apoio

## Sistema de design Microsoft Fluent 2 Dialog: um formulário dentro da estrutura compatível

### Mantenha o botão de envio dentro do formulário

A composição é formada por Dialog, DialogTrigger, DialogSurface, um elemento de formulário e DialogBody, que contém DialogTitle, DialogContent e DialogActions. O formulário envolve o corpo para que o botão de envio no rodapé participe do envio. Cancel é type="button" e fica em um DialogTrigger que fecha a caixa; caso contrário, o tipo padrão do botão poderia enviar o formulário por engano.

open e onOpenChange controlam a visibilidade. disableButtonEnhancement impede que o acionador aplique aprimoramentos redundantes a um Fluent Button que já oferece comportamento de botão. O manipulador de sucesso fecha a caixa somente depois que a validação local é aprovada. Se a validação falhar, o mesmo e-mail e a mesma permissão continuam disponíveis para correção.

Consulte a especificação de apoio

### Escolha o comportamento modal de acordo com a decisão em questão

Essa decisão rápida sobre acesso usa o tipo modal padrão. Uma caixa de diálogo não modal permitiria trabalhar em segundo plano, enquanto uma caixa de alerta é voltada a decisões urgentes e tem regras mais rígidas para ser fechada. Um formulário de acesso comum não precisa de semântica de alerta. As orientações oficiais recomendam posicionar o foco dentro da caixa de diálogo e devolvê-lo ao acionador depois que ela fechar; a implementação independente usa o elemento dialog nativo e restaura explicitamente o foco no acionador.

Consulte a especificação de apoio

## Sistema de design Microsoft Fluent 2: estilos personalizados sem comprometer o tema

### Use makeStyles e variáveis semânticas

O código completo usa makeStyles com tokens para superfícies, espaçamentos e raio. Isso mantém o painel personalizado alinhado ao provider. Um nome de classe gerado é um detalhe de implementação; não o copie do DevTools para uma folha de estilos. Para experimentar temas, altere o tema do provider e inspecione em conjunto os controles nativos e as áreas personalizadas.

A escala de duração publicada vai de 50ms a 500ms. O exemplo HTML usa durationFast, 150ms, para mudanças simples ao passar o cursor e remove essas transições quando prefers-reduced-motion está ativo. Essa é uma escolha da aplicação que usa um token disponível, não uma afirmação de que toda interação do Fluent deva durar 150ms. O conteúdo e as mensagens de erro continuam compreensíveis sem animações.

Consulte a especificação de apoio

### Teste os limites de uma interpretação

O exemplo incorporado está identificado intencionalmente como interpretação HTML/CSS, não como uma implementação nativa do Fluent. Ele demonstra os valores verificados e a interação do diálogo de acesso sem baixar um framework na visualização. Use o exemplo completo React para avaliar estilos nativos, portais, gerenciamento de foco e atualizações de componentes. Passar nas verificações de interação independentes é uma evidência para este exemplo, não uma certificação de um aplicativo de compartilhamento em produção.

Consulte a especificação de apoio

## Instalação do Sistema de Design Microsoft Fluent 2 e verificação da composição completa

### Um ponto de entrada executável, não um esboço de tarefas

A configuração se destina a um aplicativo React existente, com um elemento cujo id é root. Instale o conjunto v9 e, em seguida, adicione a entrada JSX ao aplicativo. As importações, o provider, a árvore de componentes, os manipuladores, o estado e a renderização na raiz são fornecidos em conjunto. Nenhuma API externa de compartilhamento API é chamada. Se o seu projeto inicial já renderiza App, mantenha a entrada raiz existente e exporte App em vez de criar uma segunda raiz.

Registre no arquivo de lock a versão resolvida do pacote. As referências de origem vinculadas descrevem a implementação v9 do React inspecionada, enquanto os URLs de master podem mudar. Antes de atualizar, compare o tema exportado e o comportamento exato de Field, TabList e Dialog usado aqui. A configuração evita deliberadamente combinar componentes v8 de @fluentui/react com exemplos v9.

1. Instale @fluentui/react-components@9 no projeto React existente e mantenha o arquivo de lock gerado.

2. Renderize a entrada fornecida sob um único FluentProvider, com webLightTheme como tema inicial.

3. Abra o diálogo, envie um endereço inválido, corrija-o e confirme que a pessoa adicionada aparece apenas no estado local.

4. Repita com webDarkTheme e navegação pelo teclado; verifique a associação ao painel, o anúncio do erro e o retorno do foco.

Consulte a especificação de apoio

### O que integrar em um produto real de compartilhamento

Substitua a adição local somente depois de definir o contrato do serviço: usuário autenticado, espaço de trabalho de destino, valores de permissão permitidos, resultado do convite e comportamento para duplicatas. Mantenha o diálogo aberto quando o serviço rejeitar uma solicitação e mostre o resultado confirmado, em vez de afirmar acesso de forma otimista. Essas regras estão fora do Fluent, mas o código deixa explícito o ponto de integração, sem escondê-lo atrás de um botão genérico de envio.

Consulte a especificação de apoio

## Exemplos de componentes do Sistema de Design Microsoft Fluent 2

### Exemplo interativo completo e independente

Salve o documento completo como index.html e abra-o em um navegador. Interpretação independente em HTML/CSS dos tokens web do Fluent UI React v9 e da anatomia documentada de Field, TabList e Dialog. Não é um ambiente de execução de componentes nativos do Fluent.

```
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Fluent web workspace study</title><style>
:root{color-scheme:light;--colorNeutralForeground1:#242424;--colorNeutralForeground2:#424242;--colorNeutralForeground3:#616161;--colorNeutralBackground1:#fff;--colorNeutralBackground2:#fafafa;--colorNeutralBackground3:#f5f5f5;--colorNeutralStroke1:#d1d1d1;--colorNeutralStroke2:#e0e0e0;--colorBrandBackground:#0f6cbd;--colorBrandBackgroundHover:#115ea3;--colorBrandBackgroundPressed:#0c3b5e;--colorBrandBackground2:#ebf3fc;--colorBrandForegroundLink:#115ea3;--colorNeutralForegroundOnBrand:#fff;--borderRadiusMedium:4px;--borderRadiusXLarge:8px;--spacingHorizontalS:8px;--spacingHorizontalM:12px;--spacingHorizontalL:16px;--spacingHorizontalXXL:24px;--durationFast:150ms}
[data-theme=dark]{color-scheme:dark;--colorNeutralForeground1:#fff;--colorNeutralForeground2:#d6d6d6;--colorNeutralForeground3:#adadad;--colorNeutralBackground1:#292929;--colorNeutralBackground2:#1f1f1f;--colorNeutralBackground3:#141414;--colorNeutralStroke1:#666;--colorNeutralStroke2:#525252;--colorBrandBackground:#115ea3;--colorBrandBackgroundHover:#0f6cbd;--colorBrandBackground2:#082338;--colorBrandForegroundLink:#479ef5}
*{box-sizing:border-box}body{margin:0;background:var(--colorNeutralBackground3);color:var(--colorNeutralForeground1);font-family:'Segoe UI','Segoe UI Web (West European)',-apple-system,BlinkMacSystemFont,Roboto,'Helvetica Neue',sans-serif;font-size:14px;line-height:20px}button,input,select{font:inherit}button{cursor:pointer;min-height:32px;padding:5px 12px;border:1px solid var(--colorNeutralStroke1);border-radius:var(--borderRadiusMedium);background:var(--colorNeutralBackground1);color:var(--colorNeutralForeground1);font-weight:600;transition:background var(--durationFast)}button:hover{background:var(--colorNeutralBackground3)}button:focus-visible,a:focus-visible,input:focus-visible,select:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--colorBrandForegroundLink);outline-offset:3px}.primary{background:var(--colorBrandBackground);color:var(--colorNeutralForegroundOnBrand);border-color:transparent}.primary:hover{background:var(--colorBrandBackgroundHover)}.primary:active{background:var(--colorBrandBackgroundPressed)}.topbar{display:flex;justify-content:space-between;align-items:center;padding:12px 24px;background:var(--colorNeutralBackground1);border-bottom:1px solid var(--colorNeutralStroke2);gap:16px}.brand{display:flex;align-items:center;gap:12px;font-weight:600}.mark{display:grid;grid-template-columns:repeat(2,7px);gap:3px}.mark i{width:7px;height:7px;background:var(--colorBrandBackground)}.top-note{font-size:12px;color:var(--colorNeutralForeground3)}.page{max-width:1100px;margin:auto;padding:24px}.heading{display:flex;justify-content:space-between;align-items:center;gap:16px;margin-bottom:16px}.eyebrow{font-size:12px;line-height:16px;color:var(--colorNeutralForeground3);margin-bottom:4px}h1{font-size:24px;line-height:32px;font-weight:600;margin:0 0 4px}h2{font-size:20px;line-height:28px;font-weight:600;margin:0 0 16px}h3{font-size:16px;line-height:22px;font-weight:600;margin:20px 0 8px}p{margin:0 0 12px}.muted{color:var(--colorNeutralForeground3)}.tabs{display:flex;gap:16px;border-bottom:1px solid var(--colorNeutralStroke2);margin-bottom:24px}.tab{position:relative;background:none;border:0;border-radius:0;font-weight:400;padding:12px 4px;color:var(--colorNeutralForeground2)}.tab[aria-selected=true]{font-weight:600;color:var(--colorBrandForegroundLink)}.tab[aria-selected=true]:after{content:'';position:absolute;bottom:0;left:4px;right:4px;height:3px;background:var(--colorBrandBackground);border-radius:3px}.layout{display:grid;grid-template-columns:minmax(0,1fr) 240px;gap:24px}.document,.aside,.people{background:var(--colorNeutralBackground1);border:1px solid var(--colorNeutralStroke2);border-radius:var(--borderRadiusXLarge);padding:24px}.document{max-width:720px}.docmeta{display:flex;gap:8px;align-items:center;color:var(--colorNeutralForeground3);font-size:12px;line-height:16px;margin-bottom:24px}.avatar{flex:none;display:grid;place-items:center;background:var(--colorBrandBackground2);color:var(--colorBrandForegroundLink);font-weight:600;width:32px;height:32px;border-radius:50%}.quote{padding:12px 16px;background:var(--colorNeutralBackground2);border-left:3px solid var(--colorBrandBackground);margin:16px 0}.checklist{list-style:none;padding:0;margin:12px 0}.checklist li{display:flex;gap:8px;margin:8px 0}.checklist input{accent-color:var(--colorBrandBackground);width:16px;height:16px;margin-top:2px}.aside{padding:20px;height:fit-content}.aside h2{font-size:16px;line-height:22px;margin-bottom:16px}.person{display:flex;gap:8px;align-items:center;margin-bottom:16px}.person p{margin:0}.small{font-size:12px;line-height:16px}.aside hr{border:0;border-top:1px solid var(--colorNeutralStroke2);margin:20px 0}.tag{display:inline-block;border:1px solid var(--colorNeutralStroke1);padding:2px 8px;border-radius:4px;font-size:12px;color:var(--colorNeutralForeground2)}.people-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;gap:12px}.people-head h2{margin:0}.people ul{list-style:none;padding:0;margin:0}.member{display:flex;align-items:center;gap:12px;padding:16px 0;border-top:1px solid var(--colorNeutralStroke2)}.member-info{flex:1;min-width:0;overflow-wrap:anywhere}.member-info p{margin:0}.member .tag{flex:none}.caption{margin:20px 0 0;color:var(--colorNeutralForeground3);font-size:12px;line-height:16px}.status{margin-top:16px;overflow-wrap:anywhere;color:var(--colorNeutralForeground2)}.status:empty{display:none}[hidden]{display:none!important}dialog{width:min(520px,calc(100vw - 32px));padding:24px;border:1px solid var(--colorNeutralStroke1);border-radius:8px;background:var(--colorNeutralBackground1);color:var(--colorNeutralForeground1);max-height:calc(100dvh - 32px);overflow:auto}dialog::backdrop{background:rgba(0,0,0,.4)}dialog h2{font-size:20px;line-height:28px;margin-bottom:12px}.field{margin:20px 0}.field label{display:block;font-weight:600;margin-bottom:8px}.required{color:#b10e1e}.field input,.field select{width:100%;min-height:36px;background:var(--colorNeutralBackground1);color:var(--colorNeutralForeground1);padding:7px 12px;border:1px solid var(--colorNeutralStroke1);border-bottom-color:var(--colorNeutralForeground3);border-radius:4px}.field input:focus{border-bottom:2px solid var(--colorBrandBackground);padding-bottom:6px}.hint{font-size:12px;line-height:16px;color:var(--colorNeutralForeground3);margin-top:8px}.error{font-size:12px;line-height:16px;color:#b10e1e;margin-top:8px}.field input[aria-invalid=true]{border-color:#b10e1e}.actions{display:flex;justify-content:flex-end;gap:8px;margin-top:24px}[data-theme=dark] .error,[data-theme=dark] .required{color:#ff99a4}footer{padding:0 24px 20px;text-align:center;font-size:12px;color:var(--colorNeutralForeground3)}@media(max-width:700px){.page{padding:16px}.topbar{padding:12px 16px}.top-note{display:none}.layout{grid-template-columns:1fr}.document,.people{padding:20px}.aside{display:grid;grid-template-columns:1fr 1fr;gap:8px 16px}.aside h2,.aside hr{grid-column:1/-1}.aside hr{margin:0}.heading{align-items:flex-start}.heading .primary{flex:none}.member{flex-wrap:wrap}.member .tag{margin-left:44px}.topbar button,.heading button{min-height:40px}}@media(max-width:420px){h1{font-size:20px;line-height:28px}.heading{flex-direction:column;gap:12px}.aside{display:block}.member .tag{margin-left:0}}@media(prefers-reduced-motion:reduce){*{transition:none!important}}
</style></head><body>
<header class="topbar"><div class="brand"><span class="mark" aria-hidden="true"><i></i><i></i><i></i><i></i></span>Northstar workspace <span class="top-note">/ Product team</span></div><button id="theme" aria-pressed="false">Dark theme</button></header>
<main class="page"><div class="heading"><div><div class="eyebrow">Team workspace · October launch</div><h1>Launch workspace</h1><p class="muted">One brief. A clear decision. The right people.</p></div><button class="primary" id="share">Add a person</button></div>
<div class="tabs" role="tablist" aria-label="Workspace views"><button class="tab" id="brief-tab" role="tab" aria-controls="brief-panel" aria-selected="true" tabindex="0">Brief</button><button class="tab" id="people-tab" role="tab" aria-controls="people-panel" aria-selected="false" tabindex="-1">People <span id="count">2</span></button></div>
<section id="brief-panel" role="tabpanel" aria-labelledby="brief-tab" tabindex="0"><div class="layout"><article class="document"><div class="docmeta"><span class="avatar" aria-hidden="true">AC</span><span>Alex Chen · Product design<br>Updated today at 09:30</span></div><h2>A more welcoming first project</h2><p>Help a new team move from an empty workspace to its first useful result. This review focuses on the welcome screen and the first-project setup.</p><h3>The decision</h3><div class="quote">Should a sample project be created automatically, or offered as an option?</div><p>We want a clear starting point without filling the workspace with content someone did not choose. Review both paths before the team walkthrough.</p><h3>Review checklist</h3><ul class="checklist"><li><input id="c1" type="checkbox" checked><label for="c1">Welcome copy explains the next step</label></li><li><input id="c2" type="checkbox"><label for="c2">Empty workspace offers a sample project</label></li><li><input id="c3" type="checkbox"><label for="c3">Keyboard path reaches the first project action</label></li></ul><p class="small muted">Checklist changes are local to this example.</p></article><aside class="aside"><h2>Working together</h2><div class="person"><span class="avatar" aria-hidden="true">AC</span><div><p>Alex Chen</p><span class="small muted">Workspace owner</span></div></div><div class="person"><span class="avatar" aria-hidden="true">MP</span><div><p>Maya Patel</p><span class="small muted">Can edit</span></div></div><hr><div><h3>Next review</h3><p>Thursday, 10:00<br><span class="muted small">Team walkthrough · UTC</span></p></div><div><h3>Access</h3><span class="tag">Specific people</span><p class="small muted" style="margin-top:8px">Adding a person here updates only this local preview.</p></div></aside></div></section>
<section id="people-panel" role="tabpanel" aria-labelledby="people-tab" tabindex="0" hidden><div class="people"><div class="people-head"><h2>People with access</h2><span class="tag">Local example</span></div><p class="muted">The roles below are fictional. No invitation or real access change is made.</p><ul id="members"><li class="member"><span class="avatar" aria-hidden="true">AC</span><div class="member-info"><p>Alex Chen</p><span class="small muted">alex@example.com</span></div><span class="tag">Owner</span></li><li class="member"><span class="avatar" aria-hidden="true">MP</span><div class="member-info"><p>Maya Patel</p><span class="small muted">maya@example.com</span></div><span class="tag">Can edit</span></li></ul></div></section><p class="status" id="status" role="status"></p><p class="caption">Fluent UI React v9 web-token study · HTML/CSS interpretation, not native library components. Try tabs, theme, checkboxes and the access form.</p></main>
<dialog id="access" aria-labelledby="dialog-title"><form id="access-form" novalidate><h2 id="dialog-title">Add a person to Launch workspace</h2><p class="muted">Choose an address and permission. This demonstration updates the local list only.</p><div class="field"><label for="email">Email address <span class="required" aria-hidden="true">*</span></label><input id="email" type="email" autocomplete="email" aria-required="true" aria-describedby="email-hint" autofocus><div class="hint" id="email-hint">For example, jordan@example.com</div><div class="error" id="error" role="alert" hidden></div></div><div class="field"><label for="permission">Permission</label><select id="permission" aria-describedby="permission-hint"><option>Can view</option><option>Can edit</option></select><div class="hint" id="permission-hint">Viewers can read; editors can change the brief in a real application</div></div><div class="actions"><button type="button" id="cancel">Cancel</button><button class="primary" type="submit">Add locally</button></div></form></dialog>
<script>
const $=id=>document.getElementById(id),tabs=[...document.querySelectorAll('[role=tab]')],emails=new Set(['alex@example.com','maya@example.com']);
function selectTab(tab){tabs.forEach(t=>{const chosen=t===tab;t.setAttribute('aria-selected',String(chosen));t.tabIndex=chosen?0:-1;$(t.getAttribute('aria-controls')).hidden=!chosen;});}
tabs.forEach((tab,index)=>{tab.addEventListener('click',()=>selectTab(tab));tab.addEventListener('keydown',event=>{let next;if(event.key==='ArrowRight')next=(index+1)%tabs.length;if(event.key==='ArrowLeft')next=(index+tabs.length-1)%tabs.length;if(event.key==='Home')next=0;if(event.key==='End')next=tabs.length-1;if(next!==undefined){event.preventDefault();tabs.forEach(t=>t.tabIndex=-1);tabs[next].tabIndex=0;tabs[next].focus();}});});
$('theme').onclick=()=>{const dark=document.documentElement.dataset.theme!=='dark';document.documentElement.dataset.theme=dark?'dark':'light';$('theme').textContent=dark?'Light theme':'Dark theme';$('theme').setAttribute('aria-pressed',String(dark));};
const dialog=$('access'),email=$('email'),error=$('error');function clearError(){error.hidden=true;error.textContent='';email.removeAttribute('aria-invalid');email.setAttribute('aria-describedby','email-hint');}function showError(message){error.textContent=message;error.hidden=false;email.setAttribute('aria-invalid','true');email.setAttribute('aria-describedby','email-hint error');email.focus();}
$('share').onclick=()=>{clearError();dialog.showModal();email.focus();};$('cancel').onclick=()=>dialog.close();dialog.addEventListener('close',()=>$('share').focus());dialog.addEventListener('click',event=>{if(event.target===dialog){const r=dialog.getBoundingClientRect();if(event.clientX<r.left||event.clientX>r.right||event.clientY<r.top||event.clientY>r.bottom)dialog.close();}});email.addEventListener('input',clearError);
$('access-form').addEventListener('submit',event=>{event.preventDefault();const value=email.value.trim().toLowerCase();if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)){showError('Enter an email address such as name@example.com');return;}if(emails.has(value)){showError('This address already appears in the local access list');return;}const permission=$('permission').value;emails.add(value);const item=document.createElement('li');item.className='member';const avatar=document.createElement('span');avatar.className='avatar';avatar.setAttribute('aria-hidden','true');avatar.textContent=value.slice(0,2).toUpperCase();const info=document.createElement('div');info.className='member-info';const name=document.createElement('p');name.textContent=value;const sub=document.createElement('span');sub.className='small muted';sub.textContent='Added in this local example';info.append(name,sub);const role=document.createElement('span');role.className='tag';role.textContent=permission;item.append(avatar,info,role);$('members').append(item);$('count').textContent=emails.size;$('status').textContent='Added '+value+' as '+permission.toLowerCase()+' locally. No invitation was sent.';email.value='';clearError();selectTab($('people-tab'));dialog.close();});
</script></body></html>
```

Referência de código

- O diálogo de acesso e as guias deste documento usam controles nativos de HTML; execute a entrada v9 React separada para avaliar o comportamento de FluentProvider, Field e Dialog.

### Entrada da implementação nativa completa

A implementação completa, desde a configuração, reunida aqui com as explicações específicas de API. Siga os pré-requisitos documentados de pacote e compilação na configuração.

```
// Terminal: npm install @fluentui/react-components@9
// Existing React application; index.html must contain <div id="root"></div>.
// Entry file: src/main.jsx
import * as React from 'react';
import { createRoot } from 'react-dom/client';
import {
  FluentProvider, webLightTheme, webDarkTheme, makeStyles, tokens,
  Button, Body1, Title3, TabList, Tab, Field, Input, Select,
  Dialog, DialogTrigger, DialogSurface, DialogBody, DialogTitle,
  DialogContent, DialogActions
} from '@fluentui/react-components';

const useStyles = makeStyles({
  page: { maxWidth: '880px', margin: '0 auto', padding: tokens.spacingHorizontalXXL },
  header: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: tokens.spacingHorizontalL },
  panel: { padding: tokens.spacingHorizontalXXL, marginTop: tokens.spacingVerticalL,
    backgroundColor: tokens.colorNeutralBackground2, borderRadius: tokens.borderRadiusXLarge },
  form: { display: 'grid', gap: tokens.spacingVerticalL },
  row: { display: 'flex', flexWrap: 'wrap', justifyContent: 'space-between', gap: tokens.spacingHorizontalM,
    paddingTop: tokens.spacingVerticalM, paddingBottom: tokens.spacingVerticalM },
});

function Workspace({ dark, setDark }) {
  const styles = useStyles();
  const [tab, setTab] = React.useState('brief');
  const [open, setOpen] = React.useState(false);
  const [email, setEmail] = React.useState('');
  const [permission, setPermission] = React.useState('Can view');
  const [error, setError] = React.useState('');
  const [notice, setNotice] = React.useState('');
  const [people, setPeople] = React.useState([{ email: 'alex@example.com', permission: 'Owner' }]);
  function submit(event) {
    event.preventDefault();
    const value = email.trim().toLowerCase();
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
      setError('Enter an email address such as name@example.com'); return;
    }
    if (people.some(person => person.email === value)) {
      setError('This address already appears in the local access list'); return;
    }
    setPeople(previous => [...previous, { email: value, permission }]);
    setNotice(`Added ${value} as ${permission.toLowerCase()} in this local example. No invitation was sent.`);
    setEmail(''); setError(''); setOpen(false); setTab('people');
  }
  return <main className={styles.page}>
    <div className={styles.header}>
      <Title3 as="h1">Launch workspace</Title3>
      <Button onClick={() => setDark(!dark)}>{dark ? 'Light theme' : 'Dark theme'}</Button>
    </div>
    <Body1>Project brief and access settings · local demonstration</Body1>
    <TabList aria-label="Workspace views" selectedValue={tab}
      onTabSelect={(_, data) => setTab(data.value)}>
      <Tab id="brief-tab" value="brief" aria-controls="brief-panel">Brief</Tab>
      <Tab id="people-tab" value="people" aria-controls="people-panel">People</Tab>
    </TabList>
    <section id="brief-panel" role="tabpanel" aria-labelledby="brief-tab"
      hidden={tab !== 'brief'} className={styles.panel} tabIndex={0}>
      <h2>Launch a clearer onboarding flow</h2>
      <p>Review the welcome screen and first-project setup before the team walkthrough.</p>
      <p>Decision needed: should the sample project be created automatically or offered as an option?</p>
    </section>
    <section id="people-panel" role="tabpanel" aria-labelledby="people-tab"
      hidden={tab !== 'people'} className={styles.panel} tabIndex={0}>
      <h2>People with access</h2>
      <ul>{people.map(person => <li key={person.email} className={styles.row}>
        <span>{person.email}</span><strong>{person.permission}</strong>
      </li>)}</ul>
    </section>
    <Dialog open={open} onOpenChange={(_, data) => setOpen(data.open)}>
      <DialogTrigger disableButtonEnhancement><Button appearance="primary">Add a person</Button></DialogTrigger>
      <DialogSurface>
        <form noValidate onSubmit={submit}>
          <DialogBody>
            <DialogTitle>Add a person to Launch workspace</DialogTitle>
            <DialogContent className={styles.form}>
              <p>This example updates local state only. It does not send email or change real access.</p>
              <Field label="Email address" required validationState={error ? 'error' : 'none'}
                validationMessage={error || undefined}>
                <Input type="email" value={email} onChange={(_, data) => { setEmail(data.value); setError(''); }} />
              </Field>
              <Field label="Permission" hint="Viewers can read; editors can change the brief in a real application">
                <Select value={permission} onChange={(_, data) => setPermission(data.value)}>
                  <option>Can view</option><option>Can edit</option>
                </Select>
              </Field>
            </DialogContent>
            <DialogActions>
              <DialogTrigger disableButtonEnhancement><Button type="button">Cancel</Button></DialogTrigger>
              <Button type="submit" appearance="primary">Add locally</Button>
            </DialogActions>
          </DialogBody>
        </form>
      </DialogSurface>
    </Dialog>
    <p role="status">{notice}</p>
  </main>;
}
function App() {
  const [dark, setDark] = React.useState(false);
  return <FluentProvider theme={dark ? webDarkTheme : webLightTheme}>
    <Workspace dark={dark} setDark={setDark} />
  </FluentProvider>;
}
createRoot(document.getElementById('root')).render(<App />);
```

Referência de código

- Composição completa de componentes React v9 para um aplicativo React existente. A visualização independente é uma interpretação separada em HTML/CSS. Este código realiza apenas validação local e atualizações de estado; não envia e-mails nem altera permissões reais. As APIs de origem foram verificadas; não se afirma que houve compilação com o pacote nativo até que isso seja testado no projeto de destino.

### Field + Input + Select: um formulário de acesso com rótulos

Estes trechos usam as variáveis de estado e as importações da configuração completa abaixo. Field controla a apresentação do rótulo, da dica e da validação; a aplicação controla a regra de e-mail e a verificação de duplicatas.

```
<Field label="Email address" required
  validationState={error ? 'error' : 'none'}
  validationMessage={error || undefined}>
  <Input type="email" value={email}
    onChange={(_, data) => { setEmail(data.value); setError(''); }} />
</Field>
<Field label="Permission" hint="Choose what this person can do">
  <Select value={permission}
    onChange={(_, data) => setPermission(data.value)}>
    <option>Can view</option><option>Can edit</option>
  </Select>
</Field>
```

Referência de código

- required fornece uma indicação acessível de campo obrigatório; não implementa a regra de validação da aplicação.

- O estado de validação padrão passa a ser error quando validationMessage existe. Defina none explicitamente quando não houver erro a exibir.

- onChange de Input fornece a string atual em data.value; não confunda seu manipulador com um evento simples do DOM, que contém apenas o evento.

### TabList controlado com painéis associados explicitamente

TabList controla a seleção e o comportamento das guias. Renderize os painéis de conteúdo por conta própria e mantenha seus ids e nomes acessíveis associados a cada Tab.

```
<TabList selectedValue={tab} aria-label="Workspace views"
  onTabSelect={(_, data) => setTab(data.value)}>
  <Tab id="brief-tab" value="brief" aria-controls="brief-panel">Brief</Tab>
  <Tab id="people-tab" value="people" aria-controls="people-panel">People</Tab>
</TabList>
<section id="brief-panel" role="tabpanel" aria-labelledby="brief-tab"
  hidden={tab !== 'brief'} tabIndex={0}>Project brief</section>
<section id="people-panel" role="tabpanel" aria-labelledby="people-tab"
  hidden={tab !== 'people'} tabIndex={0}>People with access</section>
```

Referência de código

- selectedValue pertence a TabList; cada Tab deve ter um valor distinto.

- selectTabOnFocus é false por padrão. O foco pelas setas e a ativação são ações separadas, a menos que esse comportamento seja alterado intencionalmente.

- As guias horizontais do Fluent não quebram linha nem rolam automaticamente; o exemplo usa dois rótulos curtos para que permaneçam visíveis.

## Configuração do agente do Sistema de Design Microsoft Fluent 2

### Ponto de partida para implementação

Composição completa de componentes React v9 para um aplicativo React existente. A visualização independente é uma interpretação separada em HTML/CSS. Este código realiza apenas validação local e atualizações de estado; não envia e-mails nem altera permissões reais. As APIs de origem foram verificadas; não se afirma que houve compilação com o pacote nativo até que isso seja testado no projeto de destino.

```
// Terminal: npm install @fluentui/react-components@9
// Existing React application; index.html must contain <div id="root"></div>.
// Entry file: src/main.jsx
import * as React from 'react';
import { createRoot } from 'react-dom/client';
import {
  FluentProvider, webLightTheme, webDarkTheme, makeStyles, tokens,
  Button, Body1, Title3, TabList, Tab, Field, Input, Select,
  Dialog, DialogTrigger, DialogSurface, DialogBody, DialogTitle,
  DialogContent, DialogActions
} from '@fluentui/react-components';

const useStyles = makeStyles({
  page: { maxWidth: '880px', margin: '0 auto', padding: tokens.spacingHorizontalXXL },
  header: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: tokens.spacingHorizontalL },
  panel: { padding: tokens.spacingHorizontalXXL, marginTop: tokens.spacingVerticalL,
    backgroundColor: tokens.colorNeutralBackground2, borderRadius: tokens.borderRadiusXLarge },
  form: { display: 'grid', gap: tokens.spacingVerticalL },
  row: { display: 'flex', flexWrap: 'wrap', justifyContent: 'space-between', gap: tokens.spacingHorizontalM,
    paddingTop: tokens.spacingVerticalM, paddingBottom: tokens.spacingVerticalM },
});

function Workspace({ dark, setDark }) {
  const styles = useStyles();
  const [tab, setTab] = React.useState('brief');
  const [open, setOpen] = React.useState(false);
  const [email, setEmail] = React.useState('');
  const [permission, setPermission] = React.useState('Can view');
  const [error, setError] = React.useState('');
  const [notice, setNotice] = React.useState('');
  const [people, setPeople] = React.useState([{ email: 'alex@example.com', permission: 'Owner' }]);
  function submit(event) {
    event.preventDefault();
    const value = email.trim().toLowerCase();
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
      setError('Enter an email address such as name@example.com'); return;
    }
    if (people.some(person => person.email === value)) {
      setError('This address already appears in the local access list'); return;
    }
    setPeople(previous => [...previous, { email: value, permission }]);
    setNotice(`Added ${value} as ${permission.toLowerCase()} in this local example. No invitation was sent.`);
    setEmail(''); setError(''); setOpen(false); setTab('people');
  }
  return <main className={styles.page}>
    <div className={styles.header}>
      <Title3 as="h1">Launch workspace</Title3>
      <Button onClick={() => setDark(!dark)}>{dark ? 'Light theme' : 'Dark theme'}</Button>
    </div>
    <Body1>Project brief and access settings · local demonstration</Body1>
    <TabList aria-label="Workspace views" selectedValue={tab}
      onTabSelect={(_, data) => setTab(data.value)}>
      <Tab id="brief-tab" value="brief" aria-controls="brief-panel">Brief</Tab>
      <Tab id="people-tab" value="people" aria-controls="people-panel">People</Tab>
    </TabList>
    <section id="brief-panel" role="tabpanel" aria-labelledby="brief-tab"
      hidden={tab !== 'brief'} className={styles.panel} tabIndex={0}>
      <h2>Launch a clearer onboarding flow</h2>
      <p>Review the welcome screen and first-project setup before the team walkthrough.</p>
      <p>Decision needed: should the sample project be created automatically or offered as an option?</p>
    </section>
    <section id="people-panel" role="tabpanel" aria-labelledby="people-tab"
      hidden={tab !== 'people'} className={styles.panel} tabIndex={0}>
      <h2>People with access</h2>
      <ul>{people.map(person => <li key={person.email} className={styles.row}>
        <span>{person.email}</span><strong>{person.permission}</strong>
      </li>)}</ul>
    </section>
    <Dialog open={open} onOpenChange={(_, data) => setOpen(data.open)}>
      <DialogTrigger disableButtonEnhancement><Button appearance="primary">Add a person</Button></DialogTrigger>
      <DialogSurface>
        <form noValidate onSubmit={submit}>
          <DialogBody>
            <DialogTitle>Add a person to Launch workspace</DialogTitle>
            <DialogContent className={styles.form}>
              <p>This example updates local state only. It does not send email or change real access.</p>
              <Field label="Email address" required validationState={error ? 'error' : 'none'}
                validationMessage={error || undefined}>
                <Input type="email" value={email} onChange={(_, data) => { setEmail(data.value); setError(''); }} />
              </Field>
              <Field label="Permission" hint="Viewers can read; editors can change the brief in a real application">
                <Select value={permission} onChange={(_, data) => setPermission(data.value)}>
                  <option>Can view</option><option>Can edit</option>
                </Select>
              </Field>
            </DialogContent>
            <DialogActions>
              <DialogTrigger disableButtonEnhancement><Button type="button">Cancel</Button></DialogTrigger>
              <Button type="submit" appearance="primary">Add locally</Button>
            </DialogActions>
          </DialogBody>
        </form>
      </DialogSurface>
    </Dialog>
    <p role="status">{notice}</p>
  </main>;
}
function App() {
  const [dark, setDark] = React.useState(false);
  return <FluentProvider theme={dark ? webDarkTheme : webLightTheme}>
    <Workspace dark={dark} setDark={setDark} />
  </FluentProvider>;
}
createRoot(document.getElementById('root')).render(<App />);
```

### Um prompt para seu projeto

```
Use Fluent UI React v9 com @fluentui/react-components. Renderize FluentProvider com webLightTheme e uma alternância para webDarkTheme. Componha o resumo com Title3 e Body1, visualizações controladas de TabList/Tab com painéis explícitos e um Dialog de acesso que contenha Field/Input/Select e um formulário real. Use tokens para superfícies e espaçamentos personalizados. Siga o exemplo de componente fornecido e os pares de tipos de execução, incluindo Subtitle1 em 20/28. Preserve entradas inválidas, rejeite endereços duplicados localmente, restaure o foco ao fechar o diálogo e descreva com clareza as adições locais. Integre um serviço de compartilhamento somente quando o contrato de autorização e resultado for fornecido.
```

## Fontes e escopo do Sistema de Design Microsoft Fluent 2

Um estudo de aplicação OpenDesign do Fluent UI React v9, não um produto da Microsoft nem um serviço de compartilhamento ativo. A visualização independente é uma interpretação em HTML/CSS de tokens web verificados e da anatomia dos componentes; o código React abaixo usa componentes nativos do Fluent. Os valores de referência refletem a captura das fontes vinculadas verificada em 3 de outubro de 2026, e não garantem que todas as versões, plataformas ou marcas usem valores idênticos.

- Entrada do pacote React v9 Instalação do provider e do conjunto.

- Arquitetura de temas Uso de tokens semânticos e temas disponíveis.

- Tipografia em tempo de execução Família tipográfica, tamanho, altura da linha e pesos.

- Mapeamento de estilos de texto Predefinições mapeadas para tokens globais.

- Escala tipográfica do design Tabela da linguagem de design; Subtitle1 difere do par em tempo de execução.

- Espaçamento em tempo de execução Nomes dos tokens horizontais e verticais.

- Orientações de layout Espaçamento global e considerações de responsividade.

- Tokens de raio Valores de forma disponíveis em tempo de execução.

- Durações de movimento Valores de duração em tempo de execução.

- Mapeamento semântico claro Atribuições de aliases neutros e de marca.

- Mapeamento semântico escuro Atribuições de aliases do tema escuro.

- Escalas de marca Valores reais das marcas web e Teams.

- Construção do tema web webLightTheme usa brandWeb.

- Uso de Field Orientações sobre rótulos, validação e composição.

- Field API Slots e contrato de componentes filhos acessíveis.

- Uso de TabList Orientações de layout e seleção de categorias.

- TabList API Opções de seleção controlada e foco.

- Uso de Dialog Orientações sobre posicionamento de formulários e interação modal.

- Dialog API Visibilidade controlada e tipos de modal.

## Recursos relacionados ao Sistema de Design Microsoft Fluent 2

### Monday Vibe

Monte um painel de entregas funcional com tokens do Vibe 4 e controles de React. Entenda a diferença entre cores semânticas de ação e rótulos do painel, os níveis de espaçamento nomeados, as formas abreviadas de fonte e a configuração explícita de CSS necessária para uma nova aplicação.

### Morningstar sistema de design

Crie uma interface de pesquisa em que os números se alinham, as cores dos recursos mantêm seu significado e a tipografia permanece legível. Explore as constantes do MDS, a adoção do Vue 3, a transição do Morningstar Intrinsic e um caderno fictício de alocação com opções de ordenação.

### Mozilla Protocol

Um guia editorial do Protocol com pilhas de fontes e escalas de espaçamento e largura de contêiner reais, uma página de leitura ilustrada e responsiva e um exemplo completo de formulário nativo de CSS.


- [Referência de implementação](https://github.com/microsoft/fluentui/blob/master/packages/react-components/react-components/README.md)
- [Fonte](https://github.com/microsoft/fluentui/blob/master/packages/tokens/src/alias/lightColor.ts)
- [Fonte](https://github.com/microsoft/fluentui/blob/master/packages/tokens/src/global/brandColors.ts)
- [Fonte](https://github.com/microsoft/fluentui/blob/master/packages/tokens/src/global/spacings.ts)
- [Fonte](https://github.com/microsoft/fluentui/blob/master/packages/tokens/src/global/fonts.ts)
- [Fonte](https://github.com/microsoft/fluentui/blob/master/packages/tokens/src/global/borderRadius.ts)
- [Fonte](https://github.com/microsoft/fluentui/blob/master/packages/tokens/src/global/durations.ts)
- [Fonte](https://github.com/microsoft/fluentui/blob/master/packages/tokens/src/alias/darkColor.ts)
- [Consulte a especificação de apoio](https://github.com/microsoft/fluentui/blob/master/packages/react-components/react-components/README.md)
- [Consulte a especificação de apoio](https://github.com/microsoft/fluentui/blob/master/docs/architecture/design-tokens.md)
- [Consulte a especificação de apoio](https://github.com/microsoft/fluentui/blob/master/packages/tokens/src/alias/lightColor.ts)
- [Consulte a especificação de apoio](https://github.com/microsoft/fluentui/blob/master/packages/tokens/src/alias/darkColor.ts)
- [Consulte a especificação de apoio](https://github.com/microsoft/fluentui/blob/master/packages/tokens/src/global/fonts.ts)
- [Consulte a especificação de apoio](https://github.com/microsoft/fluentui/blob/master/packages/tokens/src/global/typographyStyles.ts)
- [Consulte a especificação de apoio](https://github.com/microsoft/fluentui/blob/master/packages/tokens/src/global/spacings.ts)
- [Consulte a especificação de apoio](https://github.com/microsoft/fluentui/blob/master/packages/tokens/src/global/borderRadius.ts)
- [Consulte a especificação de apoio](https://github.com/microsoft/fluentui/blob/master/packages/react-components/react-tabs/library/src/components/TabList/TabList.types.ts)
- [Consulte a especificação de apoio](https://fluent2.microsoft.design/components/web/react/core/tablist/usage)
- [Consulte a especificação de apoio](https://github.com/microsoft/fluentui/blob/master/packages/react-components/react-field/library/src/components/Field/Field.types.ts)
- [Consulte a especificação de apoio](https://fluent2.microsoft.design/components/web/react/core/field/usage)
- [Consulte a especificação de apoio](https://github.com/microsoft/fluentui/blob/master/packages/react-components/react-dialog/library/src/components/Dialog/Dialog.types.ts)
- [Consulte a especificação de apoio](https://fluent2.microsoft.design/components/web/react/core/dialog/usage)
- [Consulte a especificação de apoio](https://github.com/microsoft/fluentui/blob/master/packages/tokens/src/global/durations.ts)
- [Referência de código](https://github.com/microsoft/fluentui/blob/master/packages/react-components/react-components/README.md)
- [Referência de código](https://github.com/microsoft/fluentui/blob/master/packages/react-components/react-field/library/src/components/Field/Field.types.ts)
- [Referência de código](https://github.com/microsoft/fluentui/blob/master/packages/react-components/react-tabs/library/src/components/TabList/TabList.types.ts)
- [Entrada do pacote React v9 Instalação do provider e do conjunto.](https://github.com/microsoft/fluentui/blob/master/packages/react-components/react-components/README.md)
- [Arquitetura de temas Uso de tokens semânticos e temas disponíveis.](https://github.com/microsoft/fluentui/blob/master/docs/architecture/design-tokens.md)
- [Tipografia em tempo de execução Família tipográfica, tamanho, altura da linha e pesos.](https://github.com/microsoft/fluentui/blob/master/packages/tokens/src/global/fonts.ts)
- [Mapeamento de estilos de texto Predefinições mapeadas para tokens globais.](https://github.com/microsoft/fluentui/blob/master/packages/tokens/src/global/typographyStyles.ts)
- [Escala tipográfica do design Tabela da linguagem de design; Subtitle1 difere do par em tempo de execução.](https://fluent2.microsoft.design/typography)
- [Espaçamento em tempo de execução Nomes dos tokens horizontais e verticais.](https://github.com/microsoft/fluentui/blob/master/packages/tokens/src/global/spacings.ts)
- [Orientações de layout Espaçamento global e considerações de responsividade.](https://fluent2.microsoft.design/layout)
- [Tokens de raio Valores de forma disponíveis em tempo de execução.](https://github.com/microsoft/fluentui/blob/master/packages/tokens/src/global/borderRadius.ts)
- [Durações de movimento Valores de duração em tempo de execução.](https://github.com/microsoft/fluentui/blob/master/packages/tokens/src/global/durations.ts)
- [Mapeamento semântico claro Atribuições de aliases neutros e de marca.](https://github.com/microsoft/fluentui/blob/master/packages/tokens/src/alias/lightColor.ts)
- [Mapeamento semântico escuro Atribuições de aliases do tema escuro.](https://github.com/microsoft/fluentui/blob/master/packages/tokens/src/alias/darkColor.ts)
- [Escalas de marca Valores reais das marcas web e Teams.](https://github.com/microsoft/fluentui/blob/master/packages/tokens/src/global/brandColors.ts)
- [Construção do tema web webLightTheme usa brandWeb.](https://github.com/microsoft/fluentui/blob/master/packages/tokens/src/themes/web/lightTheme.ts)
- [Uso de Field Orientações sobre rótulos, validação e composição.](https://fluent2.microsoft.design/components/web/react/core/field/usage)
- [Field API Slots e contrato de componentes filhos acessíveis.](https://github.com/microsoft/fluentui/blob/master/packages/react-components/react-field/library/src/components/Field/Field.types.ts)
- [Uso de TabList Orientações de layout e seleção de categorias.](https://fluent2.microsoft.design/components/web/react/core/tablist/usage)
- [TabList API Opções de seleção controlada e foco.](https://github.com/microsoft/fluentui/blob/master/packages/react-components/react-tabs/library/src/components/TabList/TabList.types.ts)
- [Uso de Dialog Orientações sobre posicionamento de formulários e interação modal.](https://fluent2.microsoft.design/components/web/react/core/dialog/usage)
- [Dialog API Visibilidade controlada e tipos de modal.](https://github.com/microsoft/fluentui/blob/master/packages/react-components/react-dialog/library/src/components/Dialog/Dialog.types.ts)
