Skip to main content

TailwindCSS approach

Biovity uses TailwindCSS 4 exclusively for styling. No CSS modules, styled-components, or inline styles.

Core principles

  • Utility-first: Use Tailwind utility classes for all styling
  • No CSS files: Avoid creating custom CSS files or modules
  • Consistent spacing: Use Tailwind’s spacing scale (4px increments)
  • Responsive design: Mobile-first with responsive variants

Installation and setup

TailwindCSS 4 is configured in the project:
// tailwind.config.ts
import type { Config } from "tailwindcss"

const config: Config = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./components/**/*.{js,ts,jsx,tsx,mdx}",
    "./app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {
      colors: {
        // Custom color palette
      },
    },
  },
  plugins: [],
}

export default config

Utility-first styling

Basic example

Style components directly with utility classes:
const JobCard = ({ job }: JobCardProps) => (
  <div className="rounded-lg border border-gray-200 p-6 shadow-sm hover:shadow-md transition-shadow">
    <h3 className="text-lg font-semibold text-gray-900">
      {job.titulo}
    </h3>
    <p className="text-sm text-gray-600 mt-1">
      {job.empresa}
    </p>
    <div className="flex items-center gap-2 mt-4">
      <span className="text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded">
        {job.modalidad}
      </span>
    </div>
  </div>
)

Complex layouts

Use Flexbox and Grid utilities for layouts:
const DashboardLayout = ({ children }: { children: React.ReactNode }) => (
  <div className="min-h-screen flex">
    {/* Sidebar */}
    <aside className="w-64 bg-gray-50 border-r">
      <nav className="p-4 space-y-2">
        {/* Navigation items */}
      </nav>
    </aside>

    {/* Main content */}
    <main className="flex-1 flex flex-col">
      {/* Header */}
      <header className="h-16 border-b flex items-center px-6">
        <h1 className="text-xl font-semibold">Dashboard</h1>
      </header>

      {/* Content area */}
      <div className="flex-1 p-6 overflow-auto">
        {children}
      </div>
    </main>
  </div>
)

Responsive design

Breakpoint system

Tailwind uses mobile-first breakpoints:
const Hero = () => (
  <section className="px-4 sm:px-6 lg:px-8">
    <h1 className="text-3xl sm:text-4xl md:text-5xl lg:text-7xl text-gray-900 mb-4 md:mb-6">
      Donde el talento y la ciencia se encuentran
    </h1>
    <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
      {/* Grid items */}
    </div>
  </section>
)

Breakpoint reference

  • sm: 640px
  • md: 768px
  • lg: 1024px
  • xl: 1280px
  • 2xl: 1536px

Color system

Gradient backgrounds

Biovity uses gradient backgrounds for visual hierarchy:
const Hero = () => (
  <section className="relative h-dvh w-full flex items-center justify-center">
    {/* Main gradient background */}
    <div className="absolute inset-0 bg-gradient-to-br from-blue-50 via-indigo-50 to-green-50">
      {/* Blue-Cyan gradient circles */}
      <div className="absolute top-[12%] left-[5%] w-[19rem] h-[19rem] bg-gradient-to-br from-blue-400 to-cyan-400 rounded-full opacity-20 blur-2xl" />
      <div className="absolute top-[8%] right-[6%] w-[18rem] h-[18rem] bg-gradient-to-br from-blue-400 to-cyan-400 rounded-full opacity-18 blur-2xl" />

      {/* Green-Emerald gradient circles */}
      <div className="absolute top-[75%] left-[4%] w-[20rem] h-[20rem] bg-gradient-to-br from-green-400 to-emerald-400 rounded-full opacity-21 blur-2xl" />

      {/* Violet-Purple gradient circles */}
      <div className="absolute top-[22%] right-[22%] w-[18rem] h-[18rem] bg-gradient-to-br from-violet-400 to-purple-400 rounded-full opacity-19 blur-2xl" />
    </div>
    <div className="relative z-10">
      {/* Content */}
    </div>
  </section>
)

Text gradients

Use gradient text for emphasis:
<h1 className="text-5xl font-bold">
  Donde el talento y la{" "}
  <span className="bg-gradient-to-r from-blue-600 to-green-500 bg-clip-text text-transparent">
    ciencia
  </span>{" "}
  se encuentran
</h1>

Spacing and layout

Consistent spacing

Use Tailwind’s spacing scale for consistency:
<div className="p-4">    {/* 16px all sides */}
<div className="px-6">   {/* 24px horizontal */}
<div className="py-2">   {/* 8px vertical */}
<div className="pt-8">   {/* 32px top */}

Container patterns

const PageContainer = ({ children }: { children: React.ReactNode }) => (
  <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
    {children}
  </div>
)

const Section = ({ children }: { children: React.ReactNode }) => (
  <section className="py-16 md:py-24">
    {children}
  </section>
)

Dynamic classes

Using cn utility

Use the cn utility for conditional classes:
import { cn } from "@/lib/utils"

const Button = ({ variant, className, ...props }: ButtonProps) => (
  <button
    className={cn(
      "px-4 py-2 rounded-md font-medium transition-colors",
      variant === "primary" && "bg-blue-600 text-white hover:bg-blue-700",
      variant === "secondary" && "bg-gray-200 text-gray-900 hover:bg-gray-300",
      className
    )}
    {...props}
  />
)

Class variance authority

For complex component variants, use cva:
import { cva, type VariantProps } from "class-variance-authority"

const badgeVariants = cva(
  "inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-semibold",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground",
        secondary: "bg-secondary text-secondary-foreground",
        destructive: "bg-destructive text-destructive-foreground",
        outline: "border border-input",
      },
    },
    defaultVariants: {
      variant: "default",
    },
  }
)

interface BadgeProps extends VariantProps<typeof badgeVariants> {
  children: React.ReactNode
}

const Badge = ({ variant, children }: BadgeProps) => (
  <span className={badgeVariants({ variant })}>{children}</span>
)

State-based styling

Hover, focus, and active states

<button className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 active:bg-blue-800 disabled:opacity-50 disabled:cursor-not-allowed">
  Click me
</button>

Dark mode support

<div className="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
  <p className="text-gray-600 dark:text-gray-400">Supporting dark mode</p>
</div>

Animations and transitions

Built-in transitions

<div className="transition-all duration-300 ease-in-out hover:scale-105 hover:shadow-lg">
  <img src="/image.jpg" alt="Job" className="w-full h-48 object-cover" />
</div>

Using Motion (Framer Motion)

For complex animations, use Motion:
import * as m from "motion/react-m"

const AnimatedCard = () => (
  <m.div
    initial={{ opacity: 0, y: 20 }}
    animate={{ opacity: 1, y: 0 }}
    transition={{ duration: 0.5 }}
    className="rounded-lg border p-6"
  >
    <h3>Animated Content</h3>
  </m.div>
)

Accessibility

Focus indicators

Always provide clear focus indicators:
<button className="px-4 py-2 rounded focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2">
  Accessible Button
</button>

<a href="#" className="text-blue-600 hover:text-blue-800 focus:outline-none focus:underline focus:ring-2 focus:ring-blue-500">
  Accessible Link
</a>

Screen reader utilities

<button>
  <span className="sr-only">Close menu</span>
  <svg className="w-6 h-6" {/* ... */} />
</button>

Build docs developers (and LLMs) love