DEV Community

Dailson Igo Araujo Palheta
Dailson Igo Araujo Palheta

Posted on

Ruby on Rails 8 - Frontend Rápido Usando Tailwind como um Frameworks CSS Classless

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 comando rails 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 
Enter fullscreen mode Exit fullscreen mode

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 . 
Enter fullscreen mode Exit fullscreen mode

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

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; */ 
Enter fullscreen mode Exit fullscreen mode

Crie a pasta 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; } } /* --- */ 
Enter fullscreen mode Exit fullscreen mode

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); } } 
Enter fullscreen mode Exit fullscreen mode

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; } } } } 
Enter fullscreen mode Exit fullscreen mode

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> %> 
Enter fullscreen mode Exit fullscreen mode

Alguns passos adicionais para fazer o estilo dos arquivos Tailwind customizados funcionarem.

Exibir mais …

Se você seguiu os passos anteriores, o arquivo app/assets/stylesheets/application.tailwind.css deve ter somente a linha @import "./custom_tailwind/custom1.css"; descomentada.

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:

$ bin/rails assets:precompile $ bin/dev 
Enter fullscreen mode Exit fullscreen mode

Caso o comando anterior não funcione para estilizar os elementos HTML, tente primeiro limpar os arquivos anteriores e depois precompilar novamente:

$ bin/rails assets:clobber $ bin/rails assets:precompile $ bin/dev 
Enter fullscreen mode Exit fullscreen mode

Agora sim, um HTML estilizando usando Tailwind como um framework classless 🤩

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.

Repositório

Acesse aqui o repositório com o código do tutorial

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

Top comments (0)