Este artigo é intencionalmente muito semelhante aos anteriores desta série, mas desta vez usaremos o framework Tailwind como um framework css classless. Ele foi inspirado no artigo Classless CSS based on Tailwind
Inicie um novo aplicativo Rails
- O
time
antes do comandorails
serve para exibir no final da execução do comando o seu tempo de execução. No exemplo abaixo, levou 47 segundos.
$ rails -v
Rails 8.0.0
$ time rails new classless-css-tailwind
...
real 0m47.500s
user 0m33.052s
sys 0m4.249s
O Rails 8, dentro de sua filosofia No Build, utilizará por padrão o Propshaft como biblioteca de pipeline de assets e o Importmap como biblioteca para JavaScript. O Importmap não realiza nenhum tipo de processamento do JavaScript.
Abra o projeto com o VSCode ou seu editor preferido
$ cd classless-css-tailwind && code .
Criando algumas páginas para visualizar a estilização dos elementos HTML
As páginas estão nos Passos Comuns no primeiro artigo da série.
Altere o arquivo do Tailwind app/assets/stylesheets/application.tailwind.css
Crie a pasta Exibir mais …
Altere o arquivo acima para incluir a referência aos arquivos com a estilização em Tailwind CSS. Observe que apenas o Opção 1
está descomentada.
/* INSIRA OS CSS CUSTOMIZADOS DO TAILWIND NA PARTE SUPERIOR */
/* SE O "@tailwind base", "@tailwind components" E "@tailwind utilities" NÃO ESTIVEREM COMENTADOS */
/* Opção 1: Verde */
@import "./custom_tailwind/custom1.css";
/* Opção 2: Azul */
/* @import "./custom_tailwind/custom2.css"; */
/* Opção3: Do artigo "Classless CSS based on Tailwind" */
/* https://medium.com/@AntonShevchuk/classless-css-based-on-tailwind-57d4ef745c1f */
/* @import "./custom_tailwind/custom3.css"; */
/* @tailwind base;
@tailwind components;
@tailwind utilities; */
custom_tailwind
dentro do diretório app/assets/stylesheets/
para adicionar os arquivos customizados do Tailwind.
Insira o conteúdo do 1º arquivo Tailwind customizado em custom1.css
Exibir mais …
Crie o arquivo app/assets/stylesheets/custom_tailwind/custom1.css
e copie o conteúdo a seguir.
/*
Visão geral:
Unificação de variáveis de tema (ao invés de --background-light e --background-dark, temos apenas --background, e assim por diante).
Redução de duplicações de @media (prefers-color-scheme: dark). Boa parte do tema escuro está centralizado no :root.
Usamos variáveis no lugar das cores diretas e, em alguns pontos, aproveitar a nomenclatura do Tailwind.
Caso utilize o modo escuro via classes (class="dark") em vez de prefers-color-scheme,
a lógica seria um pouco diferente (usando dark:bg-*, dark:text-*, etc.).
Mas, conforme as recomendações, mantivemos o @media (prefers-color-scheme: dark) para respeitar as preferências do usuário.
1. Variáveis de tema unificadas
Agora temos --background, --text e --accent (entre outras) em vez de --background-light, --background-dark, etc.
Isso reduz a repetição e deixa o código mais fácil de manter.
2. Menos repetições de @media (prefers-color-scheme: dark)
Quase tudo para o tema escuro foi concentrado em um único bloco, dentro do :root.
Assim, sempre que o usuário preferir o modo escuro, todas as variáveis são redefinidas.
3. Uso de variáveis complementares
Adicionamos --background-code, --border, --form-border e --focus-ring para garantir que todas as cores que possam variar
conforme o tema sejam facilmente alteradas.
4. Estilos de formulário otimizados
Em vez de separar cada tipo de input em vários blocos, unificamos a maioria deles.
Evita duplicações e mantém uma consistência de design.
---
Observações Finais
Se quiser seguir ainda mais o padrão do Tailwind sem tantas variáveis, você poderia usar as classes utilitárias padrão
(bg-gray-50, text-gray-900, dark:bg-gray-800, dark:text-gray-100, etc.).
Para quem prefere o modo escuro via classe .dark, bastaria trocar o @media (prefers-color-scheme: dark)
por seletores .dark & { ... } no arquivo e controlar o tema em JavaScript ou manualmente no HTML (<html class="dark">).
*/
/*
|-----------------------------------------------------------------------------
| IMPORTA O TAILWIND CSS
|-----------------------------------------------------------------------------
| Aqui importamos as diretivas do Tailwind para carregar o CSS base,
| componentes e utilitários. Isso garante que todas as funcionalidades
| essenciais do Tailwind sejam carregadas antes de adicionarmos
| nossas customizações.
*/
@tailwind base;
@tailwind components;
@tailwind utilities;
/*
|-----------------------------------------------------------------------------
| VARIÁVEIS CSS PARA TEMAS CLARO/ESCURO
|-----------------------------------------------------------------------------
| Agora, unificamos as variáveis para evitar duplicação. Em vez de termos
| --background-light e --background-dark, temos apenas --background e
| mudamos seu valor no @media (prefers-color-scheme: dark).
*/
:root {
/* Tema claro (default) */
--background: #ffffff; /* Fundo do site */
--text: #292929; /* Cor principal do texto */
--accent: #1a8917; /* Cor de destaque (links, botões, etc.) */
/* Variáveis complementares para uso em elementos específicos */
--background-code: #f3f4f6; /* Fundo de blocos de código no claro */
--border: #e5e7eb; /* Cor de borda padrão (claro) */
--form-border: #d1d5db; /* Cor de borda padrão para formulários (claro) */
--focus-ring: #1a8917; /* Cor do anel de foco (claro) */
}
@media (prefers-color-scheme: dark) {
:root {
/* Tema escuro */
--background: #121212; /* Fundo do site */
--text: #e6e6e6; /* Cor principal do texto */
--accent: #4caf50; /* Cor de destaque (links, botões, etc.) */
/* Variáveis complementares */
--background-code: #1f2937; /* Fundo de blocos de código no escuro */
--border: #374151; /* Cor de borda padrão (escuro) */
--form-border: #4b5563; /* Cor de borda padrão para formulários (escuro) */
--focus-ring: #4caf50; /* Cor do anel de foco (escuro) */
}
}
/*
|-----------------------------------------------------------------------------
| ESTILOS BASE
|-----------------------------------------------------------------------------
| A camada base (layer base) permite que possamos sobrescrever
| estilos padrão do browser e aplicar resets ou estéticas iniciais.
| Aqui fazemos o 'apply' de classes Tailwind diretamente em tags HTML
| para criar estilos globais.
*/
@layer base {
/*
|-----------------------------------------------------------------------------
| HTML
|-----------------------------------------------------------------------------
| O <html> recebe o antialiased (que melhora a renderização de fontes),
| além das cores de fundo e texto baseadas em nossas variáveis.
*/
html {
@apply antialiased;
background-color: var(--background);
color: var(--text);
}
/*
|-----------------------------------------------------------------------------
| BODY
|-----------------------------------------------------------------------------
| Aqui definimos o espaçamento interno (padding) para o corpo do site
| e uma largura máxima de 4xl, centralizando (mx-auto) para que o
| conteúdo não fique muito extenso em telas grandes.
*/
body {
@apply mx-auto max-w-4xl px-4 leading-relaxed sm:px-6 lg:px-8;
}
/*
|-----------------------------------------------------------------------------
| TÍTULOS (H1, H2, H3, etc.)
|-----------------------------------------------------------------------------
| Definimos tamanhos de fonte diferentes para cada nível de título,
| além de margens inferiores para separar visualmente do texto seguinte.
| Também usamos breakpoints (sm:) para modificar o tamanho em telas maiores.
*/
/* h1 {
@apply mb-8 text-4xl font-bold leading-tight sm:text-5xl;
}
h2 {
@apply mb-6 text-3xl font-bold leading-tight sm:text-4xl;
}
h3 {
@apply mb-4 text-2xl font-bold sm:text-3xl;
}
h4 {
@apply mb-4 text-xl font-bold;
}
h5 {
@apply mb-4 text-lg font-bold;
}
h6 {
@apply mb-4 text-base font-bold;
} */
/* Principais mudanças feitas:
1. Removemos os breakpoints `sm:` fixos e substituímos por `clamp()`
2. A função `clamp()` aceita três valores:
- Valor mínimo (para telas pequenas)
- Valor preferido (calculado usando viewport width)
- Valor máximo (para telas grandes)
3. A fórmula geral usada é:
- Para títulos: percentual do viewport (vw) + valor base em rem
- Para texto regular: valor menor do viewport + valor base menor
4. Os valores foram escolhidos para criar uma transição suave entre:
- Telas móveis (320px+)
- Tablets (768px+)
- Desktops (1024px+)
- Telas grandes (1440px+)
Esta implementação oferece várias vantagens:
- Transição mais suave entre tamanhos de tela
- Elimina "saltos" abruptos nos breakpoints
- Mantém a legibilidade em todos os tamanhos de tela
- Reduz a quantidade de código necessário
- Proporciona uma experiência mais fluida aos usuários */
h1 {
@apply mb-8 font-bold leading-tight;
font-size: clamp(2.25rem, 5vw + 1rem, 3.5rem);
}
h2 {
@apply mb-6 font-bold leading-tight;
font-size: clamp(1.875rem, 4vw + 0.5rem, 2.75rem);
}
h3 {
@apply mb-4 font-bold;
font-size: clamp(1.5rem, 3vw + 0.5rem, 2.25rem);
}
h4 {
@apply mb-4 font-bold;
font-size: clamp(1.25rem, 2vw + 0.5rem, 1.75rem);
}
h5 {
@apply mb-4 font-bold;
font-size: clamp(1.125rem, 1.5vw + 0.5rem, 1.5rem);
}
h6 {
@apply mb-4 font-bold;
font-size: clamp(1rem, 1vw + 0.5rem, 1.25rem);
}
/*
|-----------------------------------------------------------------------------
| PARÁGRAFOS (P) E TEXTO EM GERAL
|-----------------------------------------------------------------------------
| Damos um espaçamento (margin-bottom) e um tamanho de fonte confortável.
| sm:text-xl faz com que, em telas no breakpoint "sm" (ex: >= 640px),
| o texto fique maior.
*/
/* p {
@apply mb-6 text-lg leading-relaxed sm:text-xl;
} */
p {
@apply mb-6 leading-relaxed;
font-size: clamp(1rem, 1.5vw + 0.5rem, 1.25rem);
}
/*
|-----------------------------------------------------------------------------
| LINKS (A)
|-----------------------------------------------------------------------------
| Usamos a cor de destaque (var(--accent)) e sublinhado.
| O :hover diminui a opacidade para dar um efeito de feedback ao usuário.
*/
a {
color: var(--accent);
@apply hover:underline hover:opacity-80;
}
/*
|-----------------------------------------------------------------------------
| TEXTO EM NEGRITO (STRONG) E ITÁLICO (EM)
|-----------------------------------------------------------------------------
| Mantemos a semântica e a ênfase visualmente clara.
*/
strong {
@apply font-bold;
}
em {
@apply italic;
}
/*
|-----------------------------------------------------------------------------
| LISTAS (UL, OL)
|-----------------------------------------------------------------------------
| Definimos margens, padding à esquerda e estilos de lista (disc, decimal).
*/
ul,
ol {
@apply mb-6 pl-8;
}
/* li {
@apply mb-2 text-lg sm:text-xl;
} */
li {
@apply mb-2;
font-size: clamp(1rem, 1.5vw + 0.5rem, 1.25rem);
}
ul > li {
@apply list-disc;
}
ol > li {
@apply list-decimal;
}
/*
|-----------------------------------------------------------------------------
| BLOCOS DE CÓDIGO (PRE, CODE)
|-----------------------------------------------------------------------------
| Usados para exibir trechos de código de forma destacada.
| O overflow-x-auto faz com que apareça scroll horizontal em códigos longos.
*/
pre {
@apply mb-6 overflow-x-auto rounded-lg p-4;
background-color: var(--background-code);
}
code {
@apply rounded px-1 font-mono text-sm;
background-color: var(--background-code);
}
/*
|-----------------------------------------------------------------------------
| BLOCKQUOTE (CITAÇÕES)
|-----------------------------------------------------------------------------
| Recuo (padding-left), texto em itálico e borda esquerda na cor de destaque.
*/
blockquote {
@apply mb-6 pl-4 italic;
border-left: 4px solid var(--accent);
}
/*
|-----------------------------------------------------------------------------
| TABELAS (TABLE, TH, TD)
|-----------------------------------------------------------------------------
| Tabelas ocupando toda a largura (w-full) e sem espaços entre as células
| (border-collapse). Também definimos bordas para separar conteúdo.
*/
table {
@apply mb-6 w-full border-collapse;
}
th {
@apply p-2 text-left font-bold;
border-bottom: 2px solid var(--border);
}
td {
@apply p-2;
border-bottom: 1px solid var(--border);
}
/*
|-----------------------------------------------------------------------------
| FORMULÁRIOS (INPUT, TEXTAREA, SELECT)
|-----------------------------------------------------------------------------
| Unificamos a estilização para vários tipos de input:
| - Todos terão largura cheia (w-full), padding, bordas arredondadas e
| cor de fundo conforme o tema.
| - A borda usa nossa variável de cor de borda.
*/
input[type="email"],
input[type="password"],
input[type="search"],
input[type="text"],
input[type="url"],
input[type="number"],
input[type="date"],
input[type="datetime-local"],
input[type="month"],
input[type="week"],
input[type="time"],
input[type="tel"],
select[multiple],
input,
textarea,
select {
@apply w-full rounded-lg p-2;
background-color: var(--background);
border: 1px solid var(--form-border);
}
/*
|-----------------------------------------------------------------------------
| ESTADO DE FOCUS EM FORMULÁRIOS
|-----------------------------------------------------------------------------
| outline-none remove as bordas padrão do navegador,
| e adicionamos um box-shadow para indicar que está em foco.
*/
input[type="email"]:focus,
input[type="password"]:focus,
input[type="search"]:focus,
input[type="text"]:focus,
input[type="url"]:focus,
input[type="number"]:focus,
input[type="date"]:focus,
input[type="datetime-local"]:focus,
input[type="month"]:focus,
input[type="week"]:focus,
input[type="time"]:focus,
input[type="tel"]:focus,
select[multiple]:focus,
input:focus,
textarea:focus,
select:focus {
@apply outline-none;
box-shadow: 0 0 0 2px var(--focus-ring);
}
/*
|-----------------------------------------------------------------------------
| BOTÕES (BUTTON, INPUT[TYPE=BUTTON/SUBMIT/RESET/FILE])
|-----------------------------------------------------------------------------
| Usamos a cor de destaque (accent) para o fundo e o texto fica branco.
| Adicionamos hover e transition para ficar mais agradável ao usuário.
*/
input[type="button"],
input[type="submit"],
input[type="reset"],
::file-selector-button,
button {
@apply rounded-lg px-4 py-2 text-white transition-opacity hover:opacity-90;
background-color: var(--accent);
}
/*
|-----------------------------------------------------------------------------
| IMAGENS E MÍDIA (IMG, VIDEO, AUDIO)
|-----------------------------------------------------------------------------
| As imagens terão largura máxima de 100% (max-w-full) e altura automática
| (h-auto) para ficarem responsivas, centralizadas (ms-auto), além de bordas arredondadas (rounded-lg).
*/
img {
@apply mx-auto mb-6 h-auto max-w-full rounded-lg hover:scale-[1.02];
}
/* Figcaption (legendas de imagem) */
figcaption {
@apply mt-2 text-sm italic;
text-align: center;
}
video,
audio {
@apply mb-6 w-full;
}
/*
|-----------------------------------------------------------------------------
| DIVISORES (HR)
|-----------------------------------------------------------------------------
| Linha horizontal para separar seções de conteúdo.
| Usamos var(--border) para a cor da borda, de acordo com o tema.
*/
hr {
@apply my-8;
border-top: 1px solid var(--border);
}
}
/*
|-----------------------------------------------------------------------------
| UTILITÁRIOS PERSONALIZADOS
|-----------------------------------------------------------------------------
| A camada utilities (layer utilities) é onde definimos classes utilitárias
| adicionais, caso as classes do Tailwind não cubram nossas necessidades.
*/
@layer utilities {
/*
|-----------------------------------------------------------------------------
| CONTENT-WRAPPER
|-----------------------------------------------------------------------------
| Caso queiramos reaproveitar o max-w-4xl e o mx-auto + px-4 em um
| container específico.
*/
.content-wrapper {
@apply mx-auto max-w-4xl px-4;
}
/*
|-----------------------------------------------------------------------------
| TEXT-BALANCE
|-----------------------------------------------------------------------------
| Propriedade moderna (text-wrap: balance) que melhora a distribuição
| de palavras, mas não é suportada em todos os navegadores.
| Lembre de testar compatibilidade.
*/
.text-balance {
text-wrap: balance;
}
/*
|-----------------------------------------------------------------------------
| PROSE
|-----------------------------------------------------------------------------
| Caso queira um estilo de texto ao estilo "prose" do Tailwind, mas
| sem o limite padrão de largura.
*/
.prose {
@apply max-w-none;
}
/*
|-----------------------------------------------------------------------------
| PROSE > IMG
|-----------------------------------------------------------------------------
| Exemplo de como centralizar imagens dentro de um container .prose.
*/
.prose img {
@apply mx-auto;
}
}
/*
---
*/
Insira o conteúdo do 2º arquivo Tailwind customizado em custom2.css
Exibir mais …
Crie o arquivo app/assets/stylesheets/custom_tailwind/custom2.css
e copie o conteúdo a seguir.
/* =================================================================
CONFIGURAÇÃO DE VARIÁVEIS CSS
Definição centralizada de todas as variáveis do projeto
================================================================= */
:root {
/* Cores - Tema Claro */
--color-primary: #2563eb; /* blue-600 do Tailwind */
--color-primary-hover: #1d4ed8; /* blue-700 do Tailwind */
--color-background: #ffffff;
--color-text: #1f2937; /* gray-800 do Tailwind */
--color-text-muted: #4b5563; /* gray-600 do Tailwind */
--color-border: #d1d5db; /* gray-300 do Tailwind */
--color-input-bg: #f9fafb; /* gray-50 do Tailwind */
--color-code-bg: #f3f4f6; /* gray-100 do Tailwind */
--color-code-text: #273e65; /* blue-800 do Tailwind */
/* Espaçamento */
--spacing-base: 1rem;
--spacing-lg: 1.5rem;
--spacing-xl: 2rem;
/* Border Radius */
--radius-base: 0.375rem;
--radius-lg: 0.5rem;
/* Larguras Máximas */
--max-width-content: 48rem; /* 768px */
}
/* Configuração do tema escuro usando prefers-color-scheme */
@media (prefers-color-scheme: dark) {
:root {
/* Cores - Tema Escuro */
--color-primary: #0284c7; /* sky-600 do Tailwind */
--color-primary-hover: #6990c7; /* blue-400 do Tailwind */
--color-background: #111827; /* gray-900 do Tailwind */
--color-text: #f3f4f6; /* gray-100 do Tailwind */
--color-text-muted: #9ca3af; /* gray-400 do Tailwind */
--color-border: #374151; /* gray-700 do Tailwind */
--color-input-bg: #1f2937; /* gray-800 do Tailwind */
--color-code-bg: #1f2937; /* gray-800 do Tailwind */
--color-code-text: #e8ecf6; /* blue-100 do Tailwind */
}
}
/* Importações do Tailwind */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* =================================================================
ESTILOS BASE
Configurações globais e reset de elementos HTML
================================================================= */
@layer base {
body {
@apply mx-auto max-w-3xl px-4 leading-relaxed tracking-wide sm:px-6 md:px-8 lg:px-12;
background-color: var(--color-background);
color: var(--color-text);
font-family:
system-ui,
-apple-system,
BlinkMacSystemFont,
"Segoe UI",
Roboto,
"Helvetica Neue",
Arial,
sans-serif;
line-height: clamp(1.5, 2vw + 1.2, 1.75);
}
/* Links */
a {
color: var(--color-primary);
@apply hover:underline;
}
/* Títulos - Usando variáveis CSS para tamanhos consistentes */
h1,
h2,
h3,
h4,
h5,
h6 {
margin-top: var(--spacing-lg);
margin-bottom: var(--spacing-base);
@apply font-bold leading-tight;
}
/* Sistema de escala tipográfica responsiva */
/* h1 {
@apply text-3xl sm:text-4xl md:text-5xl;
}
h2 {
@apply text-2xl sm:text-3xl md:text-4xl;
}
h3 {
@apply text-xl sm:text-2xl md:text-3xl;
}
h4 {
@apply text-lg sm:text-xl md:text-2xl;
}
h5 {
@apply text-base sm:text-lg md:text-xl;
}
h6 {
@apply text-sm sm:text-base md:text-lg;
} */
/* Sistema de escala tipográfica fluida usando clamp() */
h1 {
font-size: clamp(2rem, 5vw + 1rem, 3.5rem);
}
h2 {
font-size: clamp(1.75rem, 4vw + 0.75rem, 3rem);
}
h3 {
font-size: clamp(1.5rem, 3vw + 0.75rem, 2.5rem);
}
h4 {
font-size: clamp(1.25rem, 2vw + 0.75rem, 2rem);
}
h5 {
font-size: clamp(1.1rem, 1.5vw + 0.75rem, 1.5rem);
}
h6 {
font-size: clamp(1rem, 1vw + 0.75rem, 1.25rem);
}
/* Elementos de texto */
/* p {
margin-bottom: var(--spacing-base);
@apply text-lg leading-relaxed;
} */
/* Parágrafos com tipografia fluida */
p {
margin-bottom: var(--spacing-base);
font-size: clamp(1rem, 1.5vw + 0.75rem, 1.25rem);
line-height: clamp(1.6, 2vw + 1.2, 1.8);
}
/* Listas */
ul,
ol {
margin-bottom: var(--spacing-base);
padding-left: var(--spacing-lg);
}
ul {
@apply list-disc;
}
ol {
@apply list-decimal;
}
/* li {
margin-bottom: calc(var(--spacing-base) * 0.5);
} */
/* Elementos de lista com tipografia fluida */
li {
margin-bottom: calc(var(--spacing-base) * 0.5);
font-size: clamp(1rem, 1.5vw + 0.75rem, 1.25rem);
}
/* Imagens responsivas */
img {
@apply h-auto w-full;
border-radius: var(--radius-lg);
margin: var(--spacing-base) 0;
}
/* Citações */
/* blockquote {
border-left: 4px solid var(--color-border);
color: var(--color-text-muted);
padding-left: var(--spacing-base);
@apply my-4 italic;
} */
/* Código inline e blocos de código */
/* code {
background-color: var(--color-code-bg);
color: var(--color-code-text);
border-radius: var(--radius-base);
@apply px-1 py-0.5 text-sm;
} */
/* pre {
background-color: var(--color-code-bg);
border-radius: var(--radius-lg);
padding: var(--spacing-base);
@apply overflow-x-auto text-sm;
} */
/* Citações com tipografia fluida */
blockquote {
border-left: 4px solid var(--color-border);
color: var(--color-text-muted);
padding-left: var(--spacing-base);
@apply my-4 italic;
font-size: clamp(1rem, 1.5vw + 0.75rem, 1.25rem);
}
/* Código inline com tipografia fluida */
code {
background-color: var(--color-code-bg);
color: var(--color-code-text);
border-radius: var(--radius-base);
@apply px-1 py-0.5;
font-size: clamp(0.875rem, 1vw + 0.5rem, 1rem);
}
/* Blocos de código com tipografia fluida */
pre {
background-color: var(--color-code-bg);
border-radius: var(--radius-lg);
padding: var(--spacing-base);
@apply overflow-x-auto;
font-size: clamp(0.875rem, 1vw + 0.5rem, 1rem);
}
/* Tabelas */
table {
@apply my-4 w-full border-collapse;
}
/* th,
td {
border-bottom: 1px solid var(--color-border);
padding: calc(var(--spacing-base) * 0.5) var(--spacing-base);
@apply text-left;
} */
/* Células de tabela com tipografia fluida */
th, td {
border-bottom: 1px solid var(--color-border);
padding: calc(var(--spacing-base) * 0.5) var(--spacing-base);
@apply text-left;
font-size: clamp(0.875rem, 1vw + 0.5rem, 1rem);
}
th {
background-color: var(--color-code-bg);
@apply font-semibold;
}
/* Formulários */
input[type="email"],
input[type="password"],
input[type="search"],
input[type="text"],
input[type="url"],
input[type="number"],
input[type="date"],
input[type="datetime-local"],
input[type="month"],
input[type="week"],
input[type="time"],
input[type="tel"],
select[multiple],
input,
textarea,
select,
button {
border-radius: var(--radius-base);
margin-bottom: var(--spacing-base);
padding: calc(var(--spacing-base) * 0.5) var(--spacing-base);
width: 100%;
border: 1px solid var(--color-border);
}
::file-selector-button {
border-radius: var(--radius-base);
margin-bottom: var(--spacing-base);
padding: calc(var(--spacing-base) * 0.5) var(--spacing-base);
border: 1px solid var(--color-border);
}
/* Campos de formulário */
input[type="email"],
input[type="password"],
input[type="search"],
input[type="text"],
input[type="url"],
input[type="number"],
input[type="date"],
input[type="datetime-local"],
input[type="month"],
input[type="week"],
input[type="time"],
input[type="tel"],
select[multiple],
input,
textarea,
select {
background-color: var(--color-input-bg);
color: var(--color-text);
}
/* Estados de foco */
input[type="email"]:focus,
input[type="password"]:focus,
input[type="search"]:focus,
input[type="text"]:focus,
input[type="url"]:focus,
input[type="number"]:focus,
input[type="date"]:focus,
input[type="datetime-local"]:focus,
input[type="month"]:focus,
input[type="week"]:focus,
input[type="time"]:focus,
input[type="tel"]:focus,
select[multiple]:focus,
input:focus,
textarea:focus,
select:focus {
@apply outline-none ring-2 ring-blue-500 focus:ring-offset-2;
}
/* Botões */
button,
input[type="button"],
input[type="submit"],
input[type="reset"],
::file-selector-button {
background-color: var(--color-primary);
color: white;
@apply cursor-pointer hover:bg-blue-700;
}
}
/* =================================================================
COMPONENTES
Classes reutilizáveis para padrões comuns de design
================================================================= */
@layer components {
.container-page {
margin: 0 auto;
max-width: var(--max-width-content);
padding: 0 var(--spacing-base);
@apply sm:px-6 md:px-8 lg:px-12;
}
.table-striped tbody tr:nth-of-type(odd) {
background-color: var(--color-input-bg);
}
.code-block {
background-color: var(--color-code-bg);
border-radius: var(--radius-lg);
padding: var(--spacing-base);
@apply overflow-x-auto text-sm;
}
}
/* =================================================================
UTILITÁRIOS
Classes utilitárias personalizadas
================================================================= */
@layer utilities {
.text-primary {
color: var(--color-primary);
}
.bg-primary {
background-color: var(--color-primary);
}
}
Insira o conteúdo do 3º arquivo Tailwind customizado em custom3.css
Exibir mais …
Crie o arquivo app/assets/stylesheets/custom_tailwind/custom3.css
e copie o conteúdo a seguir.
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
body {
@apply min-h-screen bg-gradient-to-t from-slate-50 to-slate-200 text-slate-950;
}
h1 {
@apply px-4 py-8 text-2xl;
}
h2 {
@apply px-4 py-4 text-xl;
}
h3 {
@apply px-4 pb-1 pt-2 text-xl;
}
h4,
h5,
h6 {
@apply px-4 pb-0 pt-1 text-lg;
}
a {
@apply underline decoration-sky-800 underline-offset-2;
}
a:hover {
@apply decoration-2;
}
header,
main,
footer {
@apply container max-w-3xl;
}
header {
@apply mt-4 rounded-t-lg border border-slate-300 bg-slate-50;
h1 {
@apply pb-1 text-slate-900;
}
h2 {
@apply font-normal text-slate-700;
}
p {
@apply px-4 py-4 pt-0 text-base font-normal text-slate-500;
}
}
main {
@apply border-l border-r border-slate-300 bg-white;
article {
@apply py-2;
p {
@apply p-4 px-4 text-justify text-base leading-normal;
img {
@apply float-start m-3 rounded border border-gray-300 p-1;
}
}
blockquote {
@apply mx-4 p-4 px-4 text-justify text-base leading-normal text-slate-500;
@apply border-l-4 border-l-slate-500;
}
ul {
@apply m-4 rounded border border-gray-100;
@apply divide-y divide-gray-200;
li {
@apply p-4;
p {
@apply my-0;
}
}
}
span {
@apply text-base leading-normal;
}
button[type="button"] {
@apply my-2 ml-4 rounded bg-amber-200 px-2 py-1;
&:hover {
@apply transition hover:bg-amber-500;
}
}
}
form {
@apply p-4;
fieldset {
@apply rounded border border-gray-300 p-4;
}
input,
textarea,
select,
button {
@apply my-2 rounded border border-gray-300 p-2 shadow-sm;
@apply ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-slate-800;
&:disabled {
@apply border-gray-100 ring-gray-200 focus:ring-gray-500;
&:checked {
@apply bg-gray-200;
}
}
}
input:disabled + label {
@apply text-gray-500;
}
select {
@apply w-96 rounded-md border-0 py-1.5;
}
button {
@apply transition hover:bg-slate-200;
}
}
}
footer {
@apply mb-4 rounded-b-lg border border-slate-300 bg-slate-50 p-4;
}
aside {
@apply absolute right-0 top-0 m-4 max-h-[90vh] w-96 overflow-auto p-2;
@apply rounded-md border border-slate-300 bg-white shadow;
&[id="output"] {
@apply left-0;
code {
@apply max-h-[90vh] overflow-y-scroll leading-4;
}
}
nav {
@apply top-0 flex justify-between bg-white;
a {
@apply rounded px-2 py-1 no-underline;
&:hover {
@apply transition hover:bg-slate-200;
}
&[rel="prev"]:before {
content: "←";
@apply mr-0.5;
}
&[rel="index"]:before {
content: "§";
@apply mr-0.5;
}
&[href="#"]:after {
content: "↻";
@apply ml-0.5;
}
&[rel="next"]:after {
content: "→";
@apply ml-0.5;
}
}
}
div {
@apply max-h-[80vh] overflow-y-scroll;
}
hr {
@apply mx-1 my-2;
}
p {
@apply p-2 text-base;
}
code {
@apply mt-2 block whitespace-pre-wrap px-2 py-1 leading-normal;
}
input {
@apply m-0.5 p-1 font-mono text-base;
}
label {
@apply m-0.5 px-0.5 font-mono text-base font-bold;
}
button[type="button"] {
@apply my-1 min-w-24 rounded bg-slate-100 px-2 py-1;
&:hover {
@apply transition hover:bg-slate-200;
}
}
}
}
@layer components {
code {
@apply relative inline-block rounded bg-slate-50 px-1 py-0.5 font-mono;
span {
@apply text-green-600;
}
em {
@apply text-gray-500;
}
i {
@apply not-italic text-red-800;
}
&[contenteditable] {
@apply border border-green-100 bg-green-50;
}
&[contenteditable]::after {
content: "✎";
@apply absolute right-0 top-0 m-1 inline-flex h-6 w-6 items-center justify-center;
@apply rounded-full bg-amber-100 text-sm font-semibold text-black;
}
}
.accordion {
article {
@apply p-0;
h3 {
@apply border-b border-slate-300 bg-slate-100;
@apply cursor-pointer;
background-image: url(../images/arrows.png);
background-position: 98% 12px;
background-repeat: no-repeat;
&:hover {
@apply bg-slate-200;
}
}
p {
@apply hidden border-b border-slate-300;
}
}
}
.events {
@apply list-none p-0;
li {
@apply p-[2px];
}
li span {
@apply mx-1 mr-2 inline-block min-w-6 rounded-full bg-amber-400 px-1 text-center text-white;
}
}
}
@layer utilities {
.formatter {
h1,
h2,
h3 {
@apply px-4 pb-1 pt-2 text-lg text-slate-900;
}
h1 {
&::before {
content: "<h1>";
@apply mx-2 text-base text-sky-700;
}
&::after {
content: "</h1>";
@apply mx-2 text-base text-sky-700;
}
}
h2 {
&::before {
content: "<h2>";
@apply mx-2 text-base text-sky-700;
}
&::after {
content: "</h2>";
@apply mx-2 text-base text-sky-700;
}
}
h3 {
&::before {
content: "<h3>";
@apply mx-1 ml-2 text-base text-sky-700;
}
&::after {
content: "</h3>";
@apply mx-1 text-base text-sky-700;
}
}
p {
@apply ml-2;
&::before {
content: "<p>";
@apply mr-1 text-base text-sky-700;
}
&::after {
content: "</p>";
@apply ml-1 text-base text-sky-700;
}
}
div {
@apply ml-6;
&::before {
content: "<div>";
@apply ml-1 pl-2;
@apply border-l-4 border-l-amber-500;
@apply mr-1 block text-base text-sky-700;
}
&::after {
content: "</div>";
@apply ml-1 pl-2;
@apply border-l-4 border-l-amber-500;
@apply ml-1 block text-base text-sky-700;
}
}
li {
@apply ml-8;
&::before {
content: "<li>";
@apply mr-1 text-base text-sky-700;
}
&::after {
content: "</li>";
@apply ml-1 text-base text-sky-700;
}
}
header,
main,
article,
article ul,
div,
footer {
background-image: url("data:image/svg+xml,%3Csvg width='10' height='10' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Cellipse style='fill: rgb(245, 158, 11);' cx='6' cy='1' rx='1' ry='1'/%3E%3C/svg%3E");
background-position: left;
background-repeat: repeat-y;
}
header {
&::before {
content: "<header>";
@apply ml-1 pl-2;
@apply border-l-4 border-l-amber-500;
@apply text-base text-sky-700;
}
&::after {
content: "</header>";
@apply ml-1 pl-2;
@apply border-l-4 border-l-amber-500;
@apply text-base text-sky-700;
}
}
main {
&::before {
content: "<main>";
@apply ml-1 pl-2;
@apply border-l-4 border-l-amber-500;
@apply text-base text-sky-700;
}
&::after {
content: "</main>";
@apply ml-1 pl-2;
@apply border-l-4 border-l-amber-500;
@apply text-base text-sky-700;
}
}
article {
@apply ml-6 mt-4 py-0;
&::before {
content: "<article>";
@apply ml-1 block pl-2;
@apply border-l-4 border-l-amber-500;
@apply text-base text-sky-700;
}
&::after {
content: "</article>";
@apply ml-1 block pl-2;
@apply border-l-4 border-l-amber-500;
@apply text-base text-sky-700;
}
span {
&::before {
content: "<span>";
@apply mr-1 inline-block text-base text-sky-700;
}
&::after {
content: "</span>";
@apply ml-1 inline-block text-base text-sky-700;
}
}
i {
@apply not-italic;
&::before {
content: "<i>";
@apply mr-1 inline-block text-base text-sky-700;
}
&::after {
content: "</i>";
@apply ml-1 inline-block text-base text-sky-700;
}
}
}
ul {
@apply ml-7 mt-4 py-0;
&::before {
content: "<ul>";
@apply ml-1 pl-2;
@apply border-l-4 border-l-amber-500;
@apply text-base text-sky-700;
}
&::after {
content: "</ul>";
@apply ml-1 pl-2;
@apply border-l-4 border-l-amber-500;
@apply text-base text-sky-700;
}
}
footer {
&::before {
content: "<footer>";
@apply ml-1 block pl-2;
@apply border-l-4 border-l-amber-500;
@apply text-base text-sky-700;
}
&::after {
content: "</footer>";
@apply ml-1 block pl-2;
@apply border-l-4 border-l-amber-500;
@apply text-base text-sky-700;
}
}
}
}
Remova as classes Tailwind do arquivo app/views/layouts/application.html.erb
Exibir mais …
No arquivo application.html.erb
, remova ou comente a tag <main>
, que está antes e depois da tag <%= yield %>
para que não altere o comportamento da estilização customizada que criamos para o Tailwind.
<%# <main class="container mx-auto mt-28 px-5 flex"> %>
<%= yield %>
<%# </main> %>
Alguns passos adicionais para fazer o estilo dos arquivos Tailwind customizados funcionarem.
Se você seguiu os passos anteriores, o arquivo Deve existir apenas um estilo descomentado. Para testar um outro estilo, primeiro comente o estilo atualmente em uso e descomente o outro estilo que deseja testar. Após escolher um dos estilos customizados disponíveis, execute o comando abaixo: Caso o comando anterior não funcione para estilizar os elementos HTML, tente primeiro limpar os arquivos anteriores e depois precompilar novamente:Exibir mais …
app/assets/stylesheets/application.tailwind.css
deve ter somente a linha @import "./custom_tailwind/custom1.css";
descomentada.
$ bin/rails assets:precompile
$ bin/dev
$ bin/rails assets:clobber
$ bin/rails assets:precompile
$ bin/dev
Agora sim, um HTML com estilo 🤩
Após configurar o Tailwind com as customizações acima e iniciar o servior do Rails você verá seu HTML estilizado.
Modo dark
Alguns estilos possuem a opção para modo escuro (dark mode). Para confirmar, altere o tema do seu computador nas opções de personalização de cores. Procure no Windows por Ativar modo escuro para apps
e alterne entre modo escuro ou claro. A página HTML deverá automaticamente muda após a alteração no sistema operacional, indicando que possui suporte para o modo light e dark.
Passos seguintes
[x] Organizar os estilos de acordo com sua preferência;
[x] Usar estilização a partir de arquivos CSS do projeto, sem usar CDN;
[x] Replicar a capacidade de um framework classless CSS usando Tailwind;
[-] Atualizar dinamicamente no navegador as alterações feitas no projeto usando Rails Live Reload
;
[-] Se quiser gastar um pouco mais de tempo com o frontend, verifique as opções de customização do seu estilo favorito;
Referências
- https://medium.com/@AntonShevchuk/classless-css-based-on-tailwind-57d4ef745c1f
- https://guides.rubyonrails.org/layouts_and_rendering.html
- https://dev.to/leonardorafael/the-classless-and-class-light-css-aproaches-2b98
- https://prismic.io/blog/best-css-frameworks
- https://saeedesmaili.com/notes/classless-css-libraries/
- https://dev.to/logrocket/comparing-classless-css-frameworks-3267
- https://github.com/dbohdan/classless-css
- https://github.com/troxler/awesome-css-frameworks
Top comments (0)