Skip to main content

Overview

Fataplus maintains a comprehensive UI component library that powers all frontend applications. The design system is built with modern web standards, optimized for multi-tenant environments, and seamlessly integrates with Figma designs.
All components support both light and dark themes, are fully responsive, and follow WCAG accessibility guidelines.

Design System Structure

Technology Stack

Astro

Static site generation with islands architecture

React

Interactive components and client-side functionality

Tailwind CSS

Utility-first styling with custom design tokens

Component Architecture

Design Tokens

Color System

The Fataplus color system is based on brand identity with support for tenant customization:
:root {
  /* Primary Colors - AgriTech Green */
  --primary-green: hsl(142, 76%, 36%);
  --primary-green-light: hsl(142, 76%, 46%);
  --primary-green-dark: hsl(142, 76%, 26%);
  
  /* Neutral Colors */
  --background: oklch(1 0 0);
  --foreground: oklch(0.145 0 0);
  --muted: oklch(0.97 0 0);
  --muted-foreground: oklch(0.556 0 0);
  
  /* Card & Surface */
  --card: oklch(1 0 0);
  --card-foreground: oklch(0.145 0 0);
  --border: oklch(0.922 0 0);
  
  /* Accent Colors */
  --accent: oklch(0.97 0 0);
  --accent-foreground: oklch(0.205 0 0);
  
  /* Additional Brand Colors */
  --secondary-color: #27ae60;
  --accent-color: #3498db;
  --dark-color: #2c3e50;
  
  /* Design Properties */
  --radius: 0.625rem;
  --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
  --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
  --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
}

/* Dark Theme */
[data-theme="dark"] {
  --background: oklch(0.145 0 0);
  --foreground: oklch(0.985 0 0);
  --muted: oklch(0.269 0 0);
  --muted-foreground: oklch(0.708 0 0);
  --card: oklch(0.205 0 0);
  --card-foreground: oklch(0.985 0 0);
  --border: oklch(1 0 0 / 10%);
  --accent: oklch(0.269 0 0);
  --accent-foreground: oklch(0.985 0 0);
}

Typography

:root {
  /* Font Families */
  --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
  --font-mono: 'JetBrains Mono', 'Courier New', monospace;
  
  /* Font Sizes */
  --text-xs: 0.75rem;    /* 12px */
  --text-sm: 0.875rem;   /* 14px */
  --text-base: 1rem;     /* 16px */
  --text-lg: 1.125rem;   /* 18px */
  --text-xl: 1.25rem;    /* 20px */
  --text-2xl: 1.5rem;    /* 24px */
  --text-3xl: 1.875rem;  /* 30px */
  --text-4xl: 2.25rem;   /* 36px */
  
  /* Line Heights */
  --leading-tight: 1.25;
  --leading-normal: 1.5;
  --leading-relaxed: 1.75;
}

body {
  font-family: var(--font-sans);
  line-height: var(--leading-normal);
}

Base Components

Button Component

---
interface Props {
  variant?: 'primary' | 'secondary' | 'outline' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  href?: string;
  type?: 'button' | 'submit' | 'reset';
  class?: string;
}

const { 
  variant = 'primary', 
  size = 'md', 
  href, 
  type = 'button',
  class: className = ''
} = Astro.props;

const baseStyles = 'inline-flex items-center justify-center font-medium transition-colors rounded-lg focus:outline-none focus:ring-2 focus:ring-offset-2';

const variants = {
  primary: 'bg-primary text-white hover:bg-primary-dark focus:ring-primary',
  secondary: 'bg-accent text-accent-foreground hover:bg-accent/80',
  outline: 'border-2 border-primary text-primary hover:bg-primary hover:text-white',
  ghost: 'hover:bg-accent hover:text-accent-foreground',
};

const sizes = {
  sm: 'px-3 py-1.5 text-sm',
  md: 'px-4 py-2 text-base',
  lg: 'px-6 py-3 text-lg',
};

const classes = `${baseStyles} ${variants[variant]} ${sizes[size]} ${className}`;
---

{href ? (
  <a href={href} class={classes}>
    <slot />
  </a>
) : (
  <button type={type} class={classes}>
    <slot />
  </button>
)}

Card Component

---
interface Props {
  title?: string;
  description?: string;
  hover?: boolean;
  class?: string;
}

const { 
  title, 
  description, 
  hover = false,
  class: className = '' 
} = Astro.props;

const baseStyles = 'bg-card text-card-foreground rounded-lg border border-border shadow-sm';
const hoverStyles = hover ? 'transition-all hover:shadow-lg hover:border-primary/50' : '';
const classes = `${baseStyles} ${hoverStyles} ${className}`;
---

<div class={classes}>
  {(title || description) && (
    <div class="p-6 border-b border-border">
      {title && <h3 class="text-xl font-semibold">{title}</h3>}
      {description && <p class="text-muted-foreground mt-2">{description}</p>}
    </div>
  )}
  <div class="p-6">
    <slot />
  </div>
</div>

Input Component

---
interface Props {
  type?: 'text' | 'email' | 'password' | 'number' | 'tel';
  label?: string;
  placeholder?: string;
  required?: boolean;
  error?: string;
  name: string;
  class?: string;
}

const {
  type = 'text',
  label,
  placeholder,
  required = false,
  error,
  name,
  class: className = ''
} = Astro.props;

const inputStyles = `
  w-full px-4 py-2 rounded-lg border 
  ${error ? 'border-red-500' : 'border-border'}
  bg-background text-foreground
  focus:outline-none focus:ring-2 focus:ring-primary
  placeholder:text-muted-foreground
  transition-colors
  ${className}
`;
---

<div class="space-y-2">
  {label && (
    <label for={name} class="block text-sm font-medium text-foreground">
      {label}
      {required && <span class="text-red-500 ml-1">*</span>}
    </label>
  )}
  <input
    type={type}
    id={name}
    name={name}
    placeholder={placeholder}
    required={required}
    class={inputStyles}
  />
  {error && (
    <p class="text-sm text-red-500">{error}</p>
  )}
</div>

Composite Components

---
import Button from '@/components/ui/Button.astro';

interface NavLink {
  label: string;
  href: string;
  active?: boolean;
}

const navLinks: NavLink[] = [
  { label: 'Home', href: '/' },
  { label: 'Projects', href: '/projects' },
  { label: 'Services', href: '/services' },
  { label: 'About', href: '/about' },
  { label: 'Contact', href: '/contact' },
];
---

<header class="fixed top-0 w-full z-50 bg-card/80 backdrop-blur-md border-b border-border">
  <nav class="max-w-7xl mx-auto px-6 py-4">
    <div class="flex items-center justify-between">
      <!-- Logo -->
      <a href="/" class="flex items-center gap-2 text-xl font-bold text-foreground hover:text-primary transition-colors">
        <span class="text-2xl">🌱</span>
        <span>Fataplus</span>
      </a>
      
      <!-- Navigation Links -->
      <div class="hidden md:flex items-center gap-8">
        {navLinks.map(link => (
          <a 
            href={link.href}
            class="text-foreground hover:text-primary font-medium transition-colors"
          >
            {link.label}
          </a>
        ))}
      </div>
      
      <!-- CTA Button -->
      <Button variant="primary" href="/quickstart">
        Get Started
      </Button>
    </div>
  </nav>
</header>

Project Card

---
import Card from '@/components/ui/Card.astro';
import Button from '@/components/ui/Button.astro';

interface Props {
  project: {
    id: string;
    title: string;
    description: string;
    status: 'pending' | 'active' | 'completed';
    category: string;
    created_at: string;
  };
}

const { project } = Astro.props;

const statusColors = {
  pending: 'bg-yellow-100 text-yellow-800',
  active: 'bg-green-100 text-green-800',
  completed: 'bg-blue-100 text-blue-800',
};
---

<Card hover class="h-full flex flex-col">
  <div class="flex items-start justify-between mb-4">
    <div>
      <h3 class="text-xl font-semibold mb-2">{project.title}</h3>
      <p class="text-sm text-muted-foreground">{project.category}</p>
    </div>
    <span class={`px-3 py-1 rounded-full text-xs font-medium ${statusColors[project.status]}`}>
      {project.status.charAt(0).toUpperCase() + project.status.slice(1)}
    </span>
  </div>
  
  <p class="text-foreground mb-4 flex-grow">
    {project.description}
  </p>
  
  <div class="flex items-center justify-between pt-4 border-t border-border">
    <span class="text-sm text-muted-foreground">
      {new Date(project.created_at).toLocaleDateString()}
    </span>
    <Button variant="outline" size="sm" href={`/projects/${project.id}`}>
      View Details
    </Button>
  </div>
</Card>

Component Usage

Dashboard Layout

---
import Header from '@/components/navigation/Header.astro';
import ProjectCard from '@/components/projects/ProjectCard.astro';
import Card from '@/components/ui/Card.astro';

// Fetch projects from API
const response = await fetch('https://bknd.fata.plus/api/projects');
const projects = await response.json();
---

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Dashboard - Fataplus CRM</title>
  </head>
  <body>
    <Header />
    
    <main class="max-w-7xl mx-auto px-6 py-8 mt-20">
      <h1 class="text-4xl font-bold mb-8">Dashboard</h1>
      
      <!-- Stats Cards -->
      <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
        <Card>
          <div class="text-center">
            <p class="text-3xl font-bold text-primary">{projects.length}</p>
            <p class="text-muted-foreground">Total Projects</p>
          </div>
        </Card>
        <Card>
          <div class="text-center">
            <p class="text-3xl font-bold text-green-600">
              {projects.filter(p => p.status === 'active').length}
            </p>
            <p class="text-muted-foreground">Active Projects</p>
          </div>
        </Card>
        <Card>
          <div class="text-center">
            <p class="text-3xl font-bold text-blue-600">
              {projects.filter(p => p.status === 'completed').length}
            </p>
            <p class="text-muted-foreground">Completed</p>
          </div>
        </Card>
      </div>
      
      <!-- Project Grid -->
      <h2 class="text-2xl font-bold mb-6">Your Projects</h2>
      <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
        {projects.map(project => (
          <ProjectCard project={project} />
        ))}
      </div>
    </main>
  </body>
</html>

Responsive Design

All components are built with mobile-first responsive design:
/* Mobile First Approach */
/* Default: Mobile (< 640px) */

/* Small tablets */
@media (min-width: 640px) { /* sm */ }

/* Tablets */
@media (min-width: 768px) { /* md */ }

/* Small laptops */
@media (min-width: 1024px) { /* lg */ }

/* Desktops */
@media (min-width: 1280px) { /* xl */ }

/* Large screens */
@media (min-width: 1536px) { /* 2xl */ }

Accessibility

ARIA Attributes

All interactive components include proper ARIA attributes:
<button
  type="button"
  aria-label="Close dialog"
  aria-pressed="false"
  class="..."
>
  <span aria-hidden="true">×</span>
</button>

Best Practices

Component Reusability

Build small, focused components that can be composed into larger patterns

Design Tokens

Use CSS custom properties for all design values to support theming

Semantic HTML

Use appropriate HTML elements for better accessibility and SEO

Performance

Lazy load components and optimize images for fast page loads

Next Steps

Figma Integration

Learn how components sync with Figma designs

Brand Management

Customize components for tenant-specific branding

Build docs developers (and LLMs) love