npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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/ui

Apó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:

  • Button
  • Input
  • Card
  • Accordion
  • Popover
  • Dialog
  • DropdownMenu
  • Sheet
  • Sidebar
  • Tooltip
  • Separator
  • Skeleton
  • ModeToggle
  • HandleScale
  • Icon
  • Layout
  • LayoutLogin
  • 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
                                    │
                                    ▼
                                   CGE

O 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/ui

2. 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/ui

Depois, 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 contrato

Portanto, 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
   └── ➕ Novo

O 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ário

Nã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
  ↓
Aberto

Em 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/novo

a 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/123

o 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:

Acessibilidade

Essa entrada direciona para:

/acessibilidade

O 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)
    └── Item

Regra principal

O primeiro nível de menuOptions representa grupos do menu. Os níveis internos representam opções de navegação e submenus. Itens do primeiro nível marcados com isMoreActions sã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ção

Os 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:

☰ Menu

Ao 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:

/login

ou:

/ajuda#perguntas

customContent

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 LayoutLogin

Quando 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
    └── Copyright

loginForm

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.0

E também no rodapé:

© 2018 - 2026 - Governo do Estado do Ceará.
Todos os direitos reservados — Versão: 2026.1.0

FAQ

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
  ↓
Footer

Header

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
dark

O logo da CGE e o logo do Governo são alterados de acordo com o tema.

Tema claro

Utiliza:

cge_logo.png
light-gov.svg

Tema escuro

Utiliza:

cge_logo_branca.png
dark-gov.svg

O componente também fornece o ModeToggle para alteração do tema.


Acessibilidade

O header disponibiliza o link:

Acessibilidade

que direciona para:

/acessibilidade

També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ão

Resumo 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.