@brunolucas22/ui
v0.1.23
Published
Biblioteca de componentes de interface desenvolvida para a **COTIC — Coordenadoria de Tecnologia da Informação e Comunicação da Controladoria e Ouvidoria Geral do Estado do Ceará (CGE-CE)**.
Downloads
2,788
Readme
@cotic/ui
Biblioteca de componentes de interface desenvolvida para a COTIC — Coordenadoria de Tecnologia da Informação e Comunicação da Controladoria e Ouvidoria Geral do Estado do Ceará (CGE-CE).
A biblioteca fornece componentes reutilizáveis para padronizar as interfaces dos sistemas desenvolvidos pela COTIC, utilizando uma abordagem baseada em React, Tailwind CSS, Base UI (shadcn/ui) e Lucide Icons.
O objetivo é centralizar componentes, layouts, comportamentos de acessibilidade, temas e padrões visuais utilizados pelas aplicações da COTIC.
Instalação
Para instalar a biblioteca no projeto:
npm i @cotic/uiApós a instalação, os componentes podem ser importados diretamente:
import { Button, Card, Layout, LayoutLogin, ThemeProvider } from "@cotic/ui";Configuração inicial
Antes de utilizar os componentes da biblioteca, é necessário configurar o ThemeProvider na raiz da aplicação.
Essa configuração é importante porque diversos componentes da biblioteca dependem do contexto de tema para funcionar corretamente.
Além do gerenciamento do tema claro/escuro, o ThemeProvider é utilizado pelos recursos relacionados à acessibilidade e à aparência global da aplicação.
React com main.tsx
Em aplicações Vite, normalmente o arquivo de entrada é o main.tsx.
Exemplo:
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
import { ThemeProvider } from "@cotic/ui";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<ThemeProvider defaultTheme="system">
<App />
</ThemeProvider>
</StrictMode>,
);React com index.tsx
Em projetos que utilizam index.tsx como ponto de entrada, a configuração é a mesma:
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { ThemeProvider } from "@cotic/ui";
ReactDOM.createRoot(document.getElementById("root")!).render(
<ThemeProvider defaultTheme="system">
<App />
</ThemeProvider>,
);ThemeProvider
O ThemeProvider deve envolver toda a aplicação.
<ThemeProvider defaultTheme="system">
<App />
</ThemeProvider>Dessa forma, os componentes da biblioteca conseguem acessar o contexto global de tema.
A propriedade defaultTheme aceita:
defaultTheme = "system";defaultTheme = "light";defaultTheme = "dark";Tema do sistema
<ThemeProvider defaultTheme="system">
<App />
</ThemeProvider>Nesse modo, a aplicação acompanha a preferência de tema configurada no sistema operacional/navegador.
Tema claro
<ThemeProvider defaultTheme="light">
<App />
</ThemeProvider>Tema escuro
<ThemeProvider defaultTheme="dark">
<App />
</ThemeProvider>Recomendação
Para aplicações da COTIC, quando não houver uma necessidade específica de definir um tema fixo, recomenda-se utilizar:
<ThemeProvider defaultTheme="system">Assim, a aplicação respeita inicialmente a preferência do usuário.
Acessibilidade e tema
A configuração do ThemeProvider deve ser feita antes da utilização dos componentes da biblioteca.
Uma estrutura recomendada é:
main.tsx
│
├── ThemeProvider
│ │
│ └── App
│ │
│ ├── Layout
│ ├── LayoutLogin
│ ├── Button
│ ├── Dialog
│ ├── Input
│ ├── Menu
│ └── demais componentes
│Isso garante que os componentes que dependem do contexto global possam acessar corretamente as configurações de tema.
O ThemeProvider também é utilizado pelos recursos relacionados à alternância de tema e componentes de acessibilidade disponibilizados pela biblioteca.
Estrutura recomendada da aplicação (opcional)
Uma aplicação utilizando @cotic/ui pode ter uma estrutura semelhante a:
src/
├── main.tsx
├── App.tsx
├── pages/
├── components/
├── hooks/
└── ...No main.tsx:
import { createRoot } from "react-dom/client";
import { ThemeProvider } from "@cotic/ui";
import App from "./App";
createRoot(document.getElementById("root")!).render(
<ThemeProvider defaultTheme="system">
<App />
</ThemeProvider>,
);A partir desse ponto, os componentes podem ser utilizados normalmente em qualquer parte da aplicação.
Componentes
A biblioteca disponibiliza componentes reutilizáveis para as aplicações da COTIC.
Entre eles:
ButtonInputCardAccordionPopoverDialogDropdownMenuSheetSidebarTooltipSeparatorSkeletonModeToggleHandleScaleIconLayoutLayoutLogin- entre outros.
Os componentes são projetados para trabalhar de forma integrada com o sistema de temas e os padrões visuais da biblioteca, e são feitos com base da biblioteca shadcn/ui então a forma de usar e documentação são os mesmos com exceção dos Layouts.
Layout
O Layout fornece a estrutura principal das aplicações internas da COTIC.
Ele contempla:
- Header;
- menu lateral;
- menu responsivo;
- grupos de menu;
- submenus;
- navegação;
- breadcrumb;
- ações de topo;
- controle de tema;
- acessibilidade;
- rodapé institucional;
- versão do sistema.
Exemplo:
import { Layout } from "@cotic/ui";
<Layout
logoSystem={<Logo />}
logoSystemMinimized={<LogoMinimizada />}
menuOptions={menuOptions}
content={<MinhaPagina />}
/>;Para a documentação completa, consulte a seção Layout (componente) mais adiante neste documento.
LayoutLogin
O LayoutLogin fornece a estrutura para páginas de login e páginas públicas das aplicações.
Ele contempla:
- Header;
- logo;
- menu desktop;
- menu mobile;
- formulário de login;
- background personalizado;
- FAQ;
- conteúdo personalizado por rota;
- seções adicionais;
- controle de tema;
- acessibilidade;
- rodapé institucional;
- versão do sistema.
Exemplo:
import { LayoutLogin } from "@cotic/ui";
<LayoutLogin
logoSystem={<LogoSistema />}
loginForm={<LoginForm />}
menuOptions={menuOptions}
/>;Para a documentação completa, consulte a seção LayoutLogin (componente) mais adiante neste documento.
Menus
Os componentes Layout e LayoutLogin utilizam a mesma estrutura para configuração dos menus.
export interface ItemsMenuOptions {
label?: string;
icon?: string;
to?: string;
isMoreActions?: boolean;
items?: ItemsMenuOptions[];
}Exemplo:
const menuOptions = [
{
label: "Contratos",
items: [
{
label: "Consultar contratos",
to: "/contratos",
},
{
label: "Novo contrato",
to: "/contratos/novo",
},
],
},
{
label: "Relatórios",
to: "/relatorios",
isMoreActions: true,
},
];A estrutura permite criar:
Grupo
├── Opção
├── Opção
└── Submenu
├── Opção
└── OpçãoÍcones
A biblioteca possui suporte a ícones utilizando Lucide Icons.
Também existe suporte para utilização de classes no padrão PrimeIcons através do componente Icon.
Por exemplo:
<Icon icon="pi pi-search" />ou:
<Icon icon="pi pi-fw pi-search" />Isso facilita a migração de aplicações que anteriormente utilizavam PrimeReact/PrimeIcons para a biblioteca da COTIC.
Tema
Os componentes da biblioteca possuem suporte aos temas:
- claro;
- escuro;
- sistema.
A aplicação pode controlar o tema através do ThemeProvider e do ModeToggle.
Exemplo:
<ThemeProvider defaultTheme="system">
<App />
</ThemeProvider>E, quando necessário, disponibilizar o controle de tema:
<ModeToggle />Acessibilidade
A biblioteca possui componentes e recursos voltados à acessibilidade das aplicações.
Entre os recursos utilizados pelos layouts estão:
- controle de escala da interface;
- alternância de tema;
- suporte ao tema do sistema;
- estruturas responsivas;
- componentes de interface com suporte a navegação por teclado;
- elementos semânticos de interface;
- componentes baseados em primitives acessíveis.
Para que os recursos dependentes do contexto global funcionem corretamente, a aplicação deve configurar o ThemeProvider na raiz.
Exemplo:
<ThemeProvider defaultTheme="system">
<App />
</ThemeProvider>Exemplo completo de inicialização
Uma aplicação pode ser inicializada da seguinte forma:
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { ThemeProvider } from "@cotic/ui";
import App from "./App";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<ThemeProvider defaultTheme="system">
<App />
</ThemeProvider>
</StrictMode>,
);Depois disso, os componentes podem ser utilizados normalmente:
import { Button, Layout, ModeToggle } from "@cotic/ui";Arquitetura
A biblioteca foi desenvolvida para funcionar como uma camada compartilhada entre os sistemas desenvolvidos pela COTIC.
@cotic/ui
│
┌─────────────────────────┼────────────────────────┐
│ │ │
Componentes Layouts Utilitários
│ │ │
┌──────┼──────┐ ┌───┴────┐ ┌───┴───┐
│ │ │ │ │ │ │
Button Input Card Layout LayoutLogin Icon Theme
│ │
└───┬────┘
│
Aplicações
da COTIC
│
▼
CGEO objetivo é evitar que cada sistema implemente individualmente os mesmos componentes e comportamentos.
Assim, alterações e melhorias realizadas na biblioteca podem ser reutilizadas pelas aplicações que dependem dela.
Tecnologias
A biblioteca utiliza tecnologias modernas do ecossistema React, incluindo:
- React;
- TypeScript;
- Tailwind CSS;
- Base UI (shadcn/ui);
- Lucide React;
- Class Variance Authority (CVA).
A implementação busca manter os componentes reutilizáveis, composáveis e compatíveis com a arquitetura das aplicações da COTIC.
Uso recomendado
Ao iniciar um novo projeto utilizando @cotic/ui, siga esta sequência:
1. Instale a biblioteca
npm i @cotic/ui2. Configure o ThemeProvider
No main.tsx ou index.tsx:
<ThemeProvider defaultTheme="system">
<App />
</ThemeProvider>3. Importe os componentes
import { Button, Card, Layout, LayoutLogin } from "@cotic/ui";4. Utilize os layouts e componentes
<Layout
logoSystem={<Logo />}
logoSystemMinimized={<LogoMin />}
menuOptions={menuOptions}
content={<Home />}
/>ou:
<LayoutLogin logoSystem={<Logo />} loginForm={<LoginForm />} />Boas práticas
Sempre configure o ThemeProvider na raiz
Prefira:
<ThemeProvider defaultTheme="system">
<App />
</ThemeProvider>em vez de envolver componentes individuais:
<SomeComponent>
<ThemeProvider>...</ThemeProvider>
</SomeComponent>O provider deve ficar o mais próximo possível da raiz da aplicação.
Utilize os componentes da biblioteca
Quando existir um componente equivalente na biblioteca, prefira utilizá-lo em vez de implementar uma nova versão específica dentro da aplicação.
Isso mantém:
- consistência visual;
- comportamento padronizado;
- acessibilidade;
- manutenção centralizada;
- reutilização entre sistemas.
Compatibilidade com aplicações existentes
A biblioteca foi concebida também para facilitar a migração de aplicações que anteriormente utilizavam PrimeReact.
Por isso, alguns componentes e utilitários possuem APIs que facilitam a adaptação de estruturas existentes.
Um exemplo é o suporte ao padrão de classes de ícones:
<Icon icon="pi pi-search" />que permite reduzir o impacto da migração de PrimeIcons para Lucide.
Documentação dos componentes
A documentação detalhada de cada componente de layout está organizada nas seções abaixo, dentro deste mesmo documento:
Outros componentes podem possuir documentação própria conforme a evolução da biblioteca.
Identidade da biblioteca
@cotic/ui
Biblioteca de componentes da COTIC — CGE Ceará.
Seu propósito é fornecer uma base comum para o desenvolvimento das interfaces dos sistemas da organização, promovendo padronização, reutilização, acessibilidade e consistência entre as aplicações.
Fluxo rápido
Para começar:
npm i @cotic/uiDepois, no main.tsx ou index.tsx:
import { createRoot } from "react-dom/client";
import { ThemeProvider } from "@cotic/ui";
import App from "./App";
createRoot(document.getElementById("root")!).render(
<ThemeProvider defaultTheme="system">
<App />
</ThemeProvider>,
);E então utilize os componentes:
import { Button, Layout, LayoutLogin } from "@cotic/ui";Instale → configure o ThemeProvider → utilize os componentes.
Layout (componente)
Componente responsável por fornecer a estrutura principal de navegação e conteúdo da aplicação.
O Layout centraliza elementos comuns da aplicação, como:
- Topbar;
- Logo do sistema;
- Menu lateral;
- Subtopbar;
- Breadcrumb;
- Ações da aplicação;
- Identificação do ambiente;
- Controle de tema;
- Controle de acessibilidade;
- Conteúdo principal;
- Footer;
- Versão do sistema.
O componente também possui suporte a menu minimizado, submenus recursivos e uma seção especial de Mais opções.
📋 Sumário
🧩 Visão geral
O Layout define a estrutura visual principal da aplicação.
A estrutura pode ser representada conceitualmente da seguinte forma:
[] - o que representa
() - fixo
<> - vindo de parâmetro
┌────────────────┬────────────────────────────────────────────┐
│ │ [TOPBAR] │
│ [<LOGO>] (<) <ambiente> <ações> │
│────────────────├────────────────────────────────────────────┤
│[<MENU LATERAL>]│ [SUBTOPBAR] │
│ │ <breadcrumb> <ações> (Acessibilidade) │
│ ├────────────────────────────────────────────┤
│ │ [<CONTENT>] │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ ├────────────────────────────────────────────┤
│ │ [FOOTER] │
│ │ (Logo CGE) (Suporte) │
└─────────────────────────────────────────────────────────────┘
O menu lateral pode ser alternado entre três estados:
- Aberto — menu completo;
- Minimizado — menu reduzido em telas maiores;
- Fechado — utilizado principalmente em dispositivos móveis.
📦 Importação
Exemplo:
import { Layout } from "@cotic/ui";⚙️ API
LayoutProps
type LayoutProps = {
logoSystem: ReactElement;
logoSystemMinimized: ReactElement;
menuOptions: ItemsMenuOptions[];
breadCrumb?: ReactElement;
actionsSubtopbar?: ReactElement;
env?: ReactElement | string;
content: ReactElement;
actionsTopbar?: ReactElement;
versionSystem?: string;
classNameLogo?: string;
classNameTopbar?: string;
classNameSubtopbar?: string;
classNameMenu?: string;
classNameMenuOption?: string;
classNameMenuOptionActive?: string;
classNameContent?: string;
classNameFooter?: string;
classNameToggleMenuSize?: string;
};🧭 ItemsMenuOptions
As opções utilizadas pelo menu são definidas pela interface:
export interface ItemsMenuOptions {
label?: string;
icon?: string;
to?: string;
isMoreActions?: boolean;
items?: ItemsMenuOptions[];
}| Propriedade | Tipo | Obrigatório | Descrição |
| --------------- | -------------------- | :---------: | --------------------------------------------------------------------------------- |
| label | string | ❌ | Texto apresentado para o grupo ou opção. |
| icon | string | ❌ | Ícone da opção. |
| to | string | ❌ | Rota de navegação da opção. |
| isMoreActions | boolean | ❌ | Identifica uma opção de primeiro nível que deve fazer parte de "Mais opções". |
| items | ItemsMenuOptions[] | ❌ | Opções filhas. Permite criar submenus recursivamente. |
🗂️ Estrutura do menu
É importante observar que o primeiro nível de menuOptions possui um comportamento diferente dos níveis seguintes.
Primeiro nível
No primeiro nível, cada elemento de menuOptions representa um grupo de menu.
Exemplo:
const menuOptions: ItemsMenuOptions[] = [
{
label: "Contratos",
items: [
{
label: "Consultar",
icon: "pi pi-search",
to: "/contratos",
},
{
label: "Novo contrato",
icon: "pi pi-plus",
to: "/contratos/novo",
},
],
},
];O resultado será:
CONTRATOS
🔍 Consultar
➕ Novo contratoPortanto, o label do primeiro nível é utilizado como título do grupo.
📁 Grupos de menu
Um grupo normalmente possui:
{
label: 'Contratos',
items: [...]
}Exemplo com vários grupos:
const menuOptions: ItemsMenuOptions[] = [
{
label: "Principal",
items: [
{
label: "Dashboard",
icon: "pi pi-home",
to: "/dashboard",
},
],
},
{
label: "Contratos",
items: [
{
label: "Consultar",
icon: "pi pi-search",
to: "/contratos",
},
{
label: "Novo contrato",
icon: "pi pi-plus",
to: "/contratos/novo",
},
],
},
{
label: "Administração",
items: [
{
label: "Usuários",
icon: "pi pi-users",
to: "/usuarios",
},
],
},
];Visualmente:
PRINCIPAL
🏠 Dashboard
CONTRATOS
🔍 Consultar
➕ Novo contrato
ADMINISTRAÇÃO
👥 Usuários🔗 Itens do grupo
A partir do segundo nível, uma opção pode ser um item navegável.
Exemplo:
{
label: 'Consultar',
icon: 'pi pi-search',
to: '/contratos',
}Quando o item não possui items, ele é renderizado como um link.
A navegação utiliza o valor de to.
Consultar
↓
/contratos📂 Submenus
Um item também pode possuir items.
Nesse caso, ele deixa de ser um link simples e passa a funcionar como uma opção expansível.
Exemplo:
{
label: 'Contratos',
icon: 'pi pi-file',
items: [
{
label: 'Consultar',
icon: 'pi pi-search',
to: '/contratos',
},
{
label: 'Novo',
icon: 'pi pi-plus',
to: '/contratos/novo',
},
],
}Resultado:
📄 Contratos
├── 🔍 Consultar
└── ➕ NovoO componente utiliza Accordion para controlar a expansão dessas opções.
🔄 Submenus recursivos
A propriedade:
items?: ItemsMenuOptions[]é recursiva.
Isso significa que uma opção pode possuir outras opções, que por sua vez podem possuir novas opções.
Exemplo:
const menuOptions: ItemsMenuOptions[] = [
{
label: "Administração",
items: [
{
label: "Usuários",
icon: "pi pi-users",
items: [
{
label: "Consultar",
to: "/usuarios",
},
{
label: "Novo usuário",
to: "/usuarios/novo",
},
],
},
],
},
];Estrutura:
ADMINISTRAÇÃO
└── 👥 Usuários
├── Consultar
└── Novo usuárioNão é necessário criar interfaces diferentes para cada nível.
Todos os níveis utilizam ItemsMenuOptions.
⋮ Mais opções
O Layout possui um tratamento especial para itens de primeiro nível que possuem:
isMoreActions: true;Esses itens não são renderizados como grupos normais.
O componente primeiro separa esses elementos:
menuOptions.filter((menu) => !menu.isMoreActions);Os itens com isMoreActions são agrupados separadamente e apresentados no final do menu.
Exemplo:
const menuOptions: ItemsMenuOptions[] = [
{
label: "Principal",
items: [
{
label: "Dashboard",
icon: "pi pi-home",
to: "/dashboard",
},
],
},
{
label: "Relatórios",
items: [
{
label: "Contratos",
icon: "pi pi-file",
to: "/relatorios/contratos",
},
],
},
{
label: "Configurações",
icon: "pi pi-cog",
isMoreActions: true,
items: [
{
label: "Preferências",
icon: "pi pi-sliders-h",
to: "/preferencias",
},
],
},
];O resultado conceitual será:
PRINCIPAL
🏠 Dashboard
RELATÓRIOS
📄 Contratos
MAIS OPÇÕES
Mais opções ▽Ao expandir Mais opções, os itens correspondentes são apresentados.
⚠️ Importante sobre isMoreActions
isMoreActions possui significado no primeiro nível de menuOptions.
Ele é utilizado pelo Layout para separar essas entradas dos grupos convencionais.
{
label: 'Configurações',
isMoreActions: true,
items: [...]
}Os itens internos continuam sendo tratados normalmente pelo MenuOption.
🏠 Propriedades do Layout
logoSystem
Tipo:
ReactElement;Obrigatório: Sim
Logo utilizada quando o menu está em seu estado aberto.
<Layout logoSystem={<Logo />} />logoSystemMinimized
Tipo:
ReactElement;Obrigatório: Sim
Logo utilizada quando o menu está minimizado ou fechado.
É recomendável utilizar uma versão compacta da identidade visual.
<Layout logoSystem={<Logo />} logoSystemMinimized={<LogoIcon />} />menuOptions
Tipo:
ItemsMenuOptions[]Obrigatório: Sim
Define toda a estrutura de navegação do menu.
O primeiro nível representa grupos:
const menuOptions: ItemsMenuOptions[] = [
{
label: "Contratos",
items: [
{
label: "Consultar",
to: "/contratos",
},
],
},
];breadCrumb
Tipo:
ReactElement;Obrigatório: Não
Componente exibido na Subtopbar.
<Layout breadCrumb={<Breadcrumb />} />Em telas menores, o breadcrumb é ocultado pelo próprio layout.
actionsSubtopbar
Tipo:
ReactElement;Obrigatório: Não
Permite inserir ações adicionais na Subtopbar.
<Layout
actionsSubtopbar={
<div className="flex gap-2">
<Button>Exportar</Button>
<Button>Novo</Button>
</div>
}
/>As ações são ocultadas em telas menores pelo comportamento interno do componente.
env
Tipo:
ReactElement | string;Obrigatório: Não
Identifica o ambiente atual da aplicação.
Pode ser uma string:
<Layout env="Desenvolvimento" />ou um componente:
<Layout env={<Badge variant="destructive">Homologação</Badge>} />Quando env não é informado, a área correspondente permanece vazia.
content
Tipo:
ReactElement;Obrigatório: Sim
Representa o conteúdo principal da aplicação.
<Layout content={<Dashboard />} />actionsTopbar
Tipo:
ReactElement;Obrigatório: Não
Permite adicionar ações na Topbar.
Exemplo:
<Layout
actionsTopbar={
<div className="flex items-center gap-2">
<NotificationButton />
<UserMenu />
</div>
}
/>versionSystem
Tipo:
string;Obrigatório: Não
Define a versão apresentada no Footer.
<Layout versionSystem="2026.1.0" />Quando informada, será apresentada no Footer:
© 2018 - 2026 - Governo do Estado do Ceará.
Todos os direitos reservados — Versão: 2026.1.0🎨 Personalização
O componente disponibiliza propriedades className para permitir personalização visual sem modificar sua implementação.
classNameLogo
Classes aplicadas à área da logo.
classNameLogo = "bg-red-300";classNameTopbar
Classes adicionais aplicadas à Topbar.
classNameTopbar = "bg-blue-100";classNameSubtopbar
Classes adicionais aplicadas à Subtopbar.
classNameSubtopbar = "bg-mist-100";classNameMenu
Classes adicionais aplicadas ao menu lateral.
classNameMenu = "bg-orange-200";classNameMenuOption
Classes aplicadas às opções não ativas do menu.
classNameMenuOption = "bg-green-200 hover:bg-red-500";classNameMenuOptionActive
Classes aplicadas às opções ativas do menu.
classNameMenuOptionActive = "bg-green-500";classNameContent
Classes adicionais aplicadas à área de conteúdo.
classNameContent = "bg-yellow-100";classNameFooter
Classes adicionais aplicadas ao Footer.
classNameFooter = "bg-pink-200";Essa propriedade também é utilizada na área de copyright apresentada abaixo do Footer.
classNameToggleMenuSize
Classes aplicadas ao botão que alterna o tamanho do menu.
classNameToggleMenuSize = "bg-green-100 text-blue-400";Essa classe também é aplicada ao indicador de ambiente (Badge) quando env está presente.
🚀 Exemplo básico
Uma implementação mínima do Layout:
const menuOptions: ItemsMenuOptions[] = [
{
label: "Principal",
items: [
{
label: "Dashboard",
icon: "pi pi-home",
to: "/dashboard",
},
],
},
];
return (
<Layout
logoSystem={<Logo />}
logoSystemMinimized={<LogoIcon />}
menuOptions={menuOptions}
content={<Dashboard />}
/>
);🏗️ Exemplo completo
import { Layout } from "@cotic/ui";
const menuOptions: ItemsMenuOptions[] = [
{
label: "Principal",
items: [
{
label: "Dashboard",
icon: "pi pi-home",
to: "/dashboard",
},
],
},
{
label: "Contratos",
items: [
{
label: "Consultar",
icon: "pi pi-search",
to: "/contratos",
},
{
label: "Novo contrato",
icon: "pi pi-plus",
to: "/contratos/novo",
},
{
label: "Relatórios",
icon: "pi pi-chart-bar",
items: [
{
label: "Por período",
to: "/contratos/relatorios/periodo",
},
{
label: "Por situação",
to: "/contratos/relatorios/situacao",
},
],
},
],
},
{
label: "Administração",
items: [
{
label: "Usuários",
icon: "pi pi-users",
to: "/usuarios",
},
],
},
{
label: "Configurações",
icon: "pi pi-cog",
isMoreActions: true,
items: [
{
label: "Preferências",
icon: "pi pi-sliders-h",
to: "/preferencias",
},
],
},
];
export function AppLayout() {
return (
<Layout
logoSystem={<Logo />}
logoSystemMinimized={<LogoIcon />}
menuOptions={menuOptions}
breadCrumb={<Breadcrumb />}
env="Desenvolvimento"
versionSystem="2026.1.0"
actionsTopbar={
<div className="flex items-center gap-2">
<NotificationButton />
<UserMenu />
</div>
}
actionsSubtopbar={
<div className="flex items-center gap-2">
<Button variant="outline">Exportar</Button>
<Button>Novo</Button>
</div>
}
content={<Dashboard />}
classNameLogo="bg-red-300"
classNameTopbar="bg-blue-100"
classNameMenu="bg-orange-200"
classNameToggleMenuSize="bg-green-100 text-blue-400"
classNameMenuOption="bg-green-200 hover:bg-red-500"
classNameMenuOptionActive="bg-green-500 "
classNameFooter="bg-pink-200"
classNameContent="bg-yellow-100"
classNameSubtopbar="bg-mist-100"
/>
);
}🔄 Comportamento
Controle do menu
O estado do menu é controlado internamente pelo componente.
const [openMenu, setOpenMenu] = useState<boolean>(true);O botão de alternância permite alternar entre os estados:
Aberto
↓
Minimizado
↓
AbertoEm dispositivos móveis, o menu é fechado automaticamente:
Desktop
→ menu aberto
Mobile
→ menu fechado📱 Comportamento responsivo
O componente utiliza useIsMobile() para identificar dispositivos móveis.
Quando o dispositivo é identificado como mobile:
setOpenMenu(false);Quando retorna para desktop:
setOpenMenu(true);Além disso, quando o menu está aberto em dispositivos móveis, um backdrop é exibido sobre o conteúdo:
┌──────────────────────┐
│ TOPBAR │
├──────────┬───────────┤
│ │ │
│ MENU │ BACKDROP │
│ │ │
│ │ │
└──────────┴───────────┘Clicar no backdrop fecha o menu.
🧭 Estado ativo das opções
O Layout acompanha a URL atual:
window.location.pathname + window.location.hash;Quando a URL muda através do histórico do navegador, o componente atualiza o estado interno.
Uma opção é considerada ativa quando o caminho atual começa com o valor de to.
Exemplo:
{
label: 'Contratos',
to: '/contratos',
}Para:
/contratos
/contratos/123
/contratos/novoa opção continuará sendo identificada como ativa.
📂 Estado ativo dos submenus
Um grupo expansível também pode ser considerado ativo quando qualquer uma de suas opções filhas estiver ativa.
Por exemplo:
{
label: 'Contratos',
items: [
{
label: 'Consultar',
to: '/contratos',
},
],
}Se a URL atual for:
/contratos/123o item Consultar será identificado como ativo e o grupo pai também será considerado ativo.
Essa verificação é recursiva para suportar múltiplos níveis de submenu.
🌙 Tema
O layout utiliza o tema fornecido pelo ThemeProvider.
Quando o tema configurado é system, o componente verifica a preferência do sistema operacional:
window.matchMedia("(prefers-color-scheme: dark)");Os elementos principais do layout recebem as classes necessárias para suportar os temas claro e escuro.
O controle de troca de tema é disponibilizado na Subtopbar através do ModeToggle.
♿ Acessibilidade
O layout disponibiliza uma entrada para a página de acessibilidade:
AcessibilidadeEssa entrada direciona para:
/acessibilidadeO controle de tamanho do menu também permite adaptar a navegação conforme a preferência do usuário.
🏛️ Footer
O Footer possui elementos institucionais da aplicação, incluindo:
- Logo do Governo do Estado do Ceará;
- Acesso ao CGE Atende;
- Telefone de atendimento;
- E-mail de atendimento;
- Copyright;
- Versão do sistema, quando informada.
A versão é apresentada somente quando versionSystem possui valor.
📌 Referência rápida
Layout
type LayoutProps = {
logoSystem: ReactElement;
logoSystemMinimized: ReactElement;
menuOptions: ItemsMenuOptions[];
breadCrumb?: ReactElement;
actionsSubtopbar?: ReactElement;
env?: ReactElement | string;
content: ReactElement;
actionsTopbar?: ReactElement;
versionSystem?: string;
classNameLogo?: string;
classNameTopbar?: string;
classNameSubtopbar?: string;
classNameMenu?: string;
classNameMenuOption?: string;
classNameMenuOptionActive?: string;
classNameContent?: string;
classNameFooter?: string;
classNameToggleMenuSize?: string;
};Menu
export interface ItemsMenuOptions {
label?: string;
icon?: string;
to?: string;
isMoreActions?: boolean;
items?: ItemsMenuOptions[];
}🗺️ Modelo mental do menu
A forma mais importante de entender a API é:
menuOptions
│
├── Grupo
│ ├── Item
│ ├── Item
│ └── Item
│ ├── Subitem
│ └── Subitem
│
├── Grupo
│ ├── Item
│ └── Item
│
└── Mais opções (isMoreActions)
└── ItemRegra principal
O primeiro nível de
menuOptionsrepresenta grupos do menu. Os níveis internos representam opções de navegação e submenus. Itens do primeiro nível marcados comisMoreActionssão tratados separadamente pelo Layout e agrupados na seção "Mais opções".
Essa é a principal regra estrutural a ser considerada ao montar menuOptions.
LayoutLogin (componente)
O LayoutLogin é um componente de layout destinado às páginas de login e páginas públicas da aplicação.
Ele fornece uma estrutura completa com:
- Header com logo do sistema;
- Menu responsivo;
- Menu desktop com
Popover; - Menu mobile;
- Suporte a
menuOptions; - Opção especial de Mais opções;
- Controle de tema claro/escuro;
- Acessibilidade;
- Controle de escala da interface;
- Conteúdo personalizado por rota;
- Formulário de login;
- FAQ;
- Seções adicionais;
- Rodapé institucional;
- Exibição da versão do sistema.
Importação
import { LayoutLogin } from "@cotic/ui";Props
export type LayoutLoginProps = {
actionsTopbar?: ReactElement;
logoSystem: ReactElement;
loginForm: ReactElement;
pathBackground?: string;
menuOptions?: ItemsMenuOptions[];
versionSystem?: string;
faq?: FAQ[];
otherSections?: ReactElement;
customContent?: (currentPath: string) => ReactElement | undefined;
};| Propriedade | Tipo | Obrigatória | Descrição |
| ---------------- | ---------------------------------------------------- | ----------: | --------------------------------------------------------------------- |
| actionsTopbar | ReactElement | Não | Ações adicionais exibidas no header desktop. |
| logoSystem | ReactElement | Sim | Logo exibida na área principal da página de login. |
| loginForm | ReactElement | Sim | Formulário de autenticação. |
| pathBackground | string | Não | Caminho da imagem utilizada como fundo da área de login. |
| menuOptions | ItemsMenuOptions[] | Não | Configuração dos menus da aplicação. |
| versionSystem | string | Não | Versão do sistema exibida na tela de login e no rodapé. |
| faq | FAQ[] | Não | Lista de perguntas frequentes. |
| otherSections | ReactElement | Não | Conteúdo adicional exibido após a seção de FAQ. |
| customContent | (currentPath: string) => ReactElement \| undefined | Não | Permite substituir o conteúdo padrão da página conforme a rota atual. |
ItemsMenuOptions
O LayoutLogin utiliza a mesma estrutura de menu utilizada pelo Layout.
export interface ItemsMenuOptions {
label?: string;
icon?: string;
to?: string;
isMoreActions?: boolean;
items?: ItemsMenuOptions[];
}Estrutura do menu
Os itens do primeiro nível representam grupos do menu.
const menuOptions: ItemsMenuOptions[] = [
{
label: "Contratos",
items: [
{
label: "Consultar contratos",
to: "/contratos",
},
{
label: "Novo contrato",
to: "/contratos/novo",
},
],
},
];A hierarquia é:
menuOptions
│
├── Grupo
│ ├── Opção
│ ├── Opção
│ └── Submenu
│ ├── Opção
│ └── Opção
│
└── Outro grupo
└── OpçãoOs itens internos podem ser:
- opções navegáveis através de
to; - submenus através de
items; - opções destinadas ao grupo Mais opções através de
isMoreActions.
isMoreActions
Quando isMoreActions é true, o item é tratado de maneira especial.
const menuOptions: ItemsMenuOptions[] = [
{
label: "Contratos",
items: [
{
label: "Consultar",
to: "/contratos",
},
],
},
{
label: "Relatórios",
to: "/relatorios",
isMoreActions: true,
},
{
label: "Configurações",
to: "/configuracoes",
isMoreActions: true,
},
];Esses itens não aparecem junto aos grupos principais.
No desktop, são agrupados em:
MAIS OPÇÕES ▼Ao clicar:
MENOS OPÇÕES ▲E as opções são exibidas abaixo do menu principal.
No mobile, os itens são agrupados no mesmo menu do Popover.
Menu Desktop
Em telas grandes, o menu é exibido horizontalmente.
Grupos que possuem items são apresentados através de um Popover.
Exemplo:
Contratos ▼ Consultas ▼ Administração ▼ MAIS OPÇÕES ▼Ao clicar em um grupo:
Contratos ▼
┌─────────────────────────────┐
│ Consultar contratos │
│ Novo contrato │
│ Aditivos │
└─────────────────────────────┘Itens sem items são tratados diretamente como links de navegação.
Menu Mobile
Em telas menores que o breakpoint lg, o menu desktop é substituído por um botão:
☰ MenuAo clicar, as opções são apresentadas dentro de um Popover.
O mesmo menuOptions utilizado no desktop é reutilizado no menu mobile.
Navegação
A navegação utiliza a History API do navegador.
Quando uma opção é selecionada:
window.history.pushState({}, "", item.to);Depois é disparado um evento:
const navEvent = new PopStateEvent("popstate");
window.dispatchEvent(navEvent);O LayoutLogin monitora esse evento para atualizar:
currentPath;O valor é formado por:
window.location.pathname + window.location.hash;Por exemplo:
/loginou:
/ajuda#perguntascustomContent
A propriedade customContent permite substituir completamente o conteúdo padrão da página de login.
Ela recebe a rota atual:
customContent?: (
currentPath: string
) => ReactElement | undefined;Exemplo:
<LayoutLogin
logoSystem={<Logo />}
loginForm={<LoginForm />}
customContent={(currentPath) => {
if (currentPath === "/recuperar-senha") {
return <RecuperarSenha />;
}
if (currentPath === "/cadastro") {
return <Cadastro />;
}
return undefined;
}}
/>Nesse caso:
/recuperar-senha
↓
<RecuperarSenha />
/cadastro
↓
<Cadastro />
/login
↓
conteúdo padrão do LayoutLoginQuando customContent retorna um elemento, o conteúdo padrão não é renderizado.
Ou seja, não são exibidos:
- formulário padrão;
- FAQ;
otherSections.
O conteúdo personalizado assume o lugar dessas seções.
O header e o footer continuam sendo renderizados.
Conteúdo padrão
Quando customContent não retorna conteúdo, o LayoutLogin renderiza a página padrão.
A estrutura é:
LayoutLogin
│
├── Header
│ ├── Logo
│ ├── Menu mobile
│ └── Ações
│
├── Gradient
│
├── Menu desktop
│
├── Área de login
│ ├── Logo do sistema
│ ├── Card
│ │ ├── Título
│ │ ├── Formulário
│ │ └── CGE Atende
│ └── Versão
│
├── FAQ
│
├── otherSections
│
└── Footer
├── Logo Governo
├── Informações
└── CopyrightloginForm
O loginForm representa o conteúdo do formulário de autenticação.
Exemplo:
const loginForm = (
<form>
<Input name="usuario" placeholder="Usuário" />
<Input name="senha" type="password" placeholder="Senha" />
<Button type="submit">Entrar</Button>
</form>
);Uso:
<LayoutLogin logoSystem={<Logo />} loginForm={loginForm} />O formulário é inserido dentro do CardContent do card de login.
Fundo da página
A propriedade pathBackground define a imagem de fundo da área de login.
<LayoutLogin
logoSystem={<Logo />}
loginForm={<LoginForm />}
pathBackground="/assets/login-background.jpg"
/>Caso pathBackground não seja informado, o componente utiliza seu background padrão.
Versão do sistema
A propriedade versionSystem pode ser utilizada para exibir a versão atual.
<LayoutLogin
logoSystem={<Logo />}
loginForm={<LoginForm />}
versionSystem="2026.1.0"
/>A versão será exibida na área de login:
ver: 2026.1.0E também no rodapé:
© 2018 - 2026 - Governo do Estado do Ceará.
Todos os direitos reservados — Versão: 2026.1.0FAQ
A propriedade faq permite adicionar uma seção de perguntas frequentes.
A estrutura esperada é:
export interface FAQ {
pergunta: string;
resposta: string;
}Exemplo:
const faq = [
{
pergunta: "Como recuperar minha senha?",
resposta:
"Utilize a opção de recuperação de senha disponível na tela de login.",
},
{
pergunta: "Como entrar em contato com o suporte?",
resposta: "Entre em contato através dos canais de atendimento disponíveis.",
},
];Uso:
<LayoutLogin logoSystem={<Logo />} loginForm={<LoginForm />} faq={faq} />O resultado será uma seção:
Perguntas Frequentes
Perguntas frequentes sobre o sistema
┌─────────────────────────────────────────┐
│ Como recuperar minha senha? ▼ │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ Como entrar em contato com o suporte? ▼ │
└─────────────────────────────────────────┘O FAQ utiliza Accordion e permite múltiplas perguntas abertas simultaneamente.
otherSections
otherSections permite adicionar conteúdo depois do FAQ.
<LayoutLogin
logoSystem={<Logo />}
loginForm={<LoginForm />}
faq={faq}
otherSections={
<section>
<h2>Informações importantes</h2>
<p>Consulte as informações adicionais do sistema.</p>
</section>
}
/>A ordem será:
Login
↓
FAQ
↓
otherSections
↓
FooterHeader
O header contém:
- logo da CGE;
- menu mobile;
actionsTopbar;- link de acessibilidade;
- controle de escala;
- alternância de tema.
No desktop, as ações são exibidas na lateral direita.
<LayoutLogin
logoSystem={<Logo />}
loginForm={<LoginForm />}
actionsTopbar={
<>
<Button>Ajuda</Button>
<Button>Contato</Button>
</>
}
/>Tema
O componente utiliza useTheme() e considera:
themeSimplified;Os temas disponíveis são tratados como:
light
darkO logo da CGE e o logo do Governo são alterados de acordo com o tema.
Tema claro
Utiliza:
cge_logo.png
light-gov.svgTema escuro
Utiliza:
cge_logo_branca.png
dark-gov.svgO componente também fornece o ModeToggle para alteração do tema.
Acessibilidade
O header disponibiliza o link:
Acessibilidadeque direciona para:
/acessibilidadeTambém é disponibilizado o componente:
<HandleScale />para controle da escala da interface.
Rodapé
O rodapé contém:
- logo do Governo do Estado do Ceará;
- informações de ouvidoria, ceará transparente e acesso à informação;
- canais de atendimento;
- copyright;
- versão do sistema, quando informada.
Exemplo básico
import { LayoutLogin } from "@cotic/ui";
export function Login() {
return <LayoutLogin logoSystem={<Logo />} loginForm={<LoginForm />} />;
}Exemplo com menu
const menuOptions = [
{
label: "Sistema",
items: [
{
label: "Início",
to: "/",
},
{
label: "Contratos",
to: "/contratos",
},
],
},
{
label: "Ajuda",
items: [
{
label: "Perguntas frequentes",
to: "/faq",
},
{
label: "Acessibilidade",
to: "/acessibilidade",
},
],
},
];<LayoutLogin
logoSystem={<Logo />}
loginForm={<LoginForm />}
menuOptions={menuOptions}
/>Exemplo completo
import { LayoutLogin } from "@cotic/ui";
const menuOptions = [
{
label: "Sistema",
items: [
{
label: "Início",
to: "/",
},
{
label: "Contratos",
to: "/contratos",
},
],
},
{
label: "Ajuda",
items: [
{
label: "Perguntas frequentes",
to: "/faq",
},
],
},
{
label: "Relatórios",
to: "/relatorios",
isMoreActions: true,
},
{
label: "Configurações",
to: "/configuracoes",
isMoreActions: true,
},
];
const faq = [
{
pergunta: "Como recuperar minha senha?",
resposta:
"Utilize a opção de recuperação de senha disponível na tela de login.",
},
{
pergunta: "Como entrar em contato com o suporte?",
resposta: "Utilize os canais de atendimento disponibilizados pelo sistema.",
},
];
export function LoginPage() {
return (
<LayoutLogin
logoSystem={<Logo />}
loginForm={<LoginForm />}
menuOptions={menuOptions}
versionSystem="2026.1.0"
faq={faq}
actionsTopbar={<Button variant="ghost">Ajuda</Button>}
otherSections={
<section className="p-10">
<h2 className="text-2xl font-bold">Informações importantes</h2>
</section>
}
/>
);
}Exemplo de customContent
Um caso comum é utilizar o mesmo layout para páginas públicas além do login.
<LayoutLogin
logoSystem={<Logo />}
loginForm={<LoginForm />}
menuOptions={menuOptions}
customContent={(currentPath) => {
if (currentPath === "/sobre") {
return <Sobre />;
}
if (currentPath === "/contato") {
return <Contato />;
}
return undefined;
}}
/>Assim:
/sobre
↓
<Sobre />
/contato
↓
<Contato />
/login
↓
Login padrãoResumo das propriedades
| Propriedade | Finalidade |
| ---------------- | ---------------------------------------- |
| logoSystem | Logo apresentada na área de login |
| loginForm | Formulário de autenticação |
| actionsTopbar | Ações adicionais no header |
| pathBackground | Background da tela de login |
| menuOptions | Menus desktop e mobile |
| versionSystem | Versão do sistema |
| faq | Perguntas frequentes |
| otherSections | Seções adicionais |
| customContent | Substituição do conteúdo padrão por rota |
Estrutura recomendada
Uma utilização típica do componente fica:
<LayoutLogin
logoSystem={<Logo />}
loginForm={<LoginForm />}
menuOptions={menuOptions}
actionsTopbar={<TopbarActions />}
versionSystem={version}
faq={faq}
otherSections={<OtherSections />}
customContent={customContent}
/>O componente centraliza, dessa forma, toda a estrutura visual das páginas públicas/login, enquanto a aplicação permanece responsável por fornecer o formulário, menus, conteúdo adicional e conteúdo específico de cada rota.
