Skip to content

Instantly share code, notes, and snippets.

@planetoftheweb
Created January 1, 2026 20:26
Show Gist options
  • Select an option

  • Save planetoftheweb/f60734434991a92cc36d1766fa545107 to your computer and use it in GitHub Desktop.

Select an option

Save planetoftheweb/f60734434991a92cc36d1766fa545107 to your computer and use it in GitHub Desktop.

VibeIt Design Guidelines

Color Palette

All colors are defined in tailwind.config.js under colors.vibe:

Name Hex Usage
backgroundDark #0b0f14 Dark mode page backgrounds
backgroundLight #EBE5D6 Light mode page backgrounds (warm cream, never pure white)
cinnabarRed #E54D42 Leaderboard accent, destructive actions, errors, "downvote" states
carrotOrange #F08D33 Default section headings (Latest Apps, Top Apps, etc.), labels
saffronYellow #F5C34D Primary CTAs, bold text in dark mode, light theme toggle
persianGreen #44A8A5 Community accent, links, focus states, input borders, success
powderBlue #91D8DB Links in dark mode, dark theme toggle, secondary accents
charcoalSlate #384955 Borders, subtle backgrounds, bold text in light mode
retroPurple #8B5FC5 My Apps / Create & Organize accent - rich retro purple

Context-Aware Color System

Color Hierarchy

Orange is the default. Use it for any UI element not in a specific feature context.

Context Color Variable When to Use
Default / Generic Carrot Orange vibe-carrotOrange Homepage sections, Latest Apps, Top Apps, generic headings
My Apps / Profile Retro Purple vibe-retroPurple Profile page, app cards on profile, Create & Organize
Community / Explore Persian Green vibe-persianGreen Community page, app cards in explore, public browsing
Leaderboard / Voting Cinnabar Red vibe-cinnabarRed Leaderboard page, vote-focused UI, rankings

Applying Context Colors

When building a page or component, determine the context and apply its color to:

  • Section headings: text-vibe-[color]
  • Card borders: border-vibe-[color]/30 (normal), border-vibe-[color]/60 (hover)
  • Card shadows: shadow-vibe-[color]/10 (normal), shadow-vibe-[color]/20 (hover)
  • Card gradients: from-vibe-[color]/15 in light, from-vibe-[color]/10 in dark
  • Buttons: bg-vibe-[color] text-white
  • Navigation active states: Uses the feature's color

Typography & Readability

Core Principles

  1. Readability first. When there's space, use it for comfortable reading. Never sacrifice legibility for compactness.

  2. Use the space. On larger screens, scale UP - bigger fonts, more padding, wider content areas. Don't force content into narrow columns when there's room to breathe.

  3. Proportional scaling. As screens get larger, everything should grow proportionally - fonts, spacing, containers. Don't cap content width too aggressively.

Responsive Scaling Philosophy

Mobile (< 640px):     Comfortable minimums, efficient use of limited space
Tablet (640-1024px):  Slightly larger, more breathing room
Desktop (1024-1280px): Generous sizing, comfortable reading
Large (1280px+):      Even larger fonts, more padding, use the space
Ultra-wide (1920px+): Maximum comfort, sidebars appear, content stays generous

Key Rule: When a sidebar appears, the main content should still feel spacious - don't shrink fonts just to fit. Instead, let the layout breathe.

Minimum Text Sizes

Element Minimum Preferred Max on Desktop
Body text text-sm (14px) text-base (16px) text-lg (18px)
Card descriptions text-sm (14px) text-base (16px) text-base
Labels/captions text-xs (12px) text-sm (14px) text-sm
Headings (h3) text-lg (18px) text-xl (20px) text-2xl
Section headings (h2) text-2xl (24px) text-3xl (30px) text-4xl

Rules

  1. Never go below text-xs (12px) for any readable text
  2. Body text should be text-base (16px) or larger when space allows
  3. On wide screens, scale UP not down - use the extra space for comfort
  4. Mobile text should match desktop - don't shrink text for mobile, shrink containers instead
  5. Line height matters - use leading-relaxed for paragraphs

Responsive Text Patterns

// DO: Keep text readable at all sizes
className="text-base md:text-lg"  // Starts at 16px, goes to 18px

// DON'T: Shrink text for mobile
className="text-xs sm:text-sm md:text-base"  // Too small on mobile ❌

// DO: Comfortable card descriptions
className="text-gray-600 dark:text-white/80 leading-relaxed"

// DON'T: Cramped sidebar text
className="text-[10px]"  // Never use arbitrary tiny sizes ❌

Sidebar & Compact Areas

Even in sidebars and compact layouts:

  • App names: minimum text-sm (14px), prefer text-base
  • Descriptions: minimum text-sm, can truncate with line-clamp-2
  • Metadata (votes, dates): text-sm is acceptable
  • When space is tight: truncate content, don't shrink font

Critical Rules

1. Always Check Both Light AND Dark Mode

Every component and page MUST be tested in both modes. Use these patterns:

// Text colors - ALWAYS include dark variant
className="text-gray-900 dark:text-white"           // Primary text
className="text-gray-600 dark:text-gray-400"        // Secondary text
className="text-gray-500 dark:text-gray-500"        // Muted text

// Backgrounds - NEVER use pure white in light mode
className="bg-vibe-backgroundLight dark:bg-vibe-backgroundDark"  // Page backgrounds
className="bg-white/60 dark:bg-gray-900/60"                       // Card/input backgrounds

// Borders
className="border-vibe-charcoalSlate/20 dark:border-gray-700"    // Subtle borders
className="border-vibe-carrotOrange/50 dark:border-gray-600"     // Hover states

2. Heading & Label Colors

  • System UI headings (Overview, Tags, Status, Notes, etc.): text-vibe-carrotOrange
  • Page titles: text-gray-900 dark:text-white
  • Form labels: text-vibe-carrotOrange (uppercase, tracking-wide)
  • Markdown H1: text-vibe-cinnabarRed dark:text-vibe-saffronYellow
  • Markdown H2: text-vibe-carrotOrange (same both modes)
  • Markdown H3: text-vibe-persianGreen dark:text-vibe-powderBlue

3. Bold Text Styling

Bold text (**text** in markdown) should stand out:

  • Light mode: text-vibe-charcoalSlate (dark slate)
  • Dark mode: text-vibe-saffronYellow (warm yellow)
className="[&_strong]:text-vibe-charcoalSlate dark:[&_strong]:text-vibe-saffronYellow"

4. Link Colors

// Standard links
className="text-vibe-persianGreen dark:text-vibe-powderBlue hover:underline"

// Navigation active state
className="text-vibe-carrotOrange"

5. Button Patterns

// Primary CTA (yellow, high visibility)
className="bg-vibe-saffronYellow text-vibe-backgroundDark hover:bg-vibe-carrotOrange"

// Secondary/outline
className="border border-vibe-charcoalSlate/30 text-gray-900 dark:text-white hover:bg-vibe-carrotOrange/10"

// Destructive
className="bg-vibe-cinnabarRed/15 text-vibe-cinnabarRed border border-vibe-cinnabarRed/30"

6. Input Field Patterns

const inputClasses = `
  w-full px-4 py-3 rounded-xl 
  border-2 border-vibe-charcoalSlate/20 dark:border-gray-700 
  bg-white/60 dark:bg-gray-900/60 
  text-gray-900 dark:text-white 
  placeholder-gray-400 dark:placeholder-gray-500 
  focus:border-vibe-persianGreen focus:bg-white dark:focus:bg-gray-900 
  outline-none transition-all
`

7. Error States

// Error container
className="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 text-red-700 dark:text-red-400 rounded-xl"

// Error text
className="text-red-500 dark:text-red-400"

8. Feature Card Pattern (Reference: "How It Works" section)

Cards should be styled based on their context color. The pattern:

// Feature card with context color (replace [color] with: retroPurple, persianGreen, cinnabarRed, or carrotOrange)
<div className="
  group block overflow-hidden rounded-2xl 
  border border-vibe-[color]/30 hover:border-vibe-[color]/60 
  bg-gradient-to-b from-vibe-[color]/15 via-vibe-backgroundLight to-vibe-backgroundLight 
  dark:from-vibe-[color]/10 dark:via-vibe-backgroundDark dark:to-vibe-backgroundDark 
  shadow-lg shadow-vibe-[color]/10 hover:shadow-xl hover:shadow-vibe-[color]/20 
  transition-all duration-300 hover:-translate-y-1
">
  {/* Card headline in context color */}
  <h3 className="text-xl font-semibold text-vibe-[color]">Title</h3>
  
  {/* Body text - always readable */}
  <p className="text-gray-900 dark:text-white/80">Description</p>
  
  {/* Button in context color */}
  <button className="bg-vibe-[color] text-white hover:bg-vibe-[color]/80">
    Action
  </button>
</div>

Card Color Quick Reference

Page/Section Color Variable Border Shadow Heading
Homepage (Latest Apps) carrotOrange /30/60 /10/20 Orange
Profile / My Apps retroPurple /30/60 /10/20 Purple
Community / Explore persianGreen /30/60 /10/20 Green
Leaderboard cinnabarRed /30/60 /10/20 Red

Checklist Before Shipping

Before submitting any UI changes, verify:

  • Light mode readable: All text has sufficient contrast against backgroundLight
  • Dark mode readable: All text has sufficient contrast against backgroundDark
  • No pure white backgrounds: Use bg-vibe-backgroundLight or semi-transparent whites
  • Inputs styled for both modes: Check placeholder, text, border, and focus states
  • Errors visible in both modes: Red tones should work on both backgrounds
  • Links distinguishable: Persian green (light) / Powder blue (dark)
  • Headings use palette colors: Carrot orange for system labels
  • Navigation visibility: Check fixed nav against various page backgrounds

9. Icon Consistency

Use consistent icons across the application. The canonical icon set is defined in src/components/Navigation.tsx:

Feature Icon Description
My Apps / Create & Organize Boxes/storage icon Multiple stacked boxes
Community People group icon Group of 3 people
Leaderboard Bar chart icon Ascending bars
User Person silhouette Single person
Visit App External link Arrow pointing out of box
Star Star outline/filled 5-point star
Vote up Arrow up Upward pointing arrow
Vote down (remove) Arrow down Downward pointing arrow
Public/visible Eye open Eye with pupil
Private/hidden Eye closed Eye with slash

Rules:

  • Always use stroke="currentColor" for icons (not fill) unless intentionally filled
  • Use strokeWidth={2} as default
  • Size icons consistently: h-4 w-4 for buttons, h-5 w-5 for navigation
  • When adding icons to buttons, match size and styling of existing buttons

10. Custom Tooltips (NEVER use native title)

CRITICAL RULE: Never use the HTML title attribute for tooltips. Always use custom CSS tooltips for a consistent, styled experience.

Why No Native Tooltips?

  • Native tooltips have inconsistent styling across browsers
  • They appear with delay and cannot be styled
  • They don't work on touch devices
  • They break the visual consistency of the app

Custom Tooltip Pattern

All interactive elements that need hover hints must use this pattern:

// Container with group class
<div className="relative group">
  {/* Interactive element - NO title attribute */}
  <button className="..." aria-label="Description for accessibility">
    <Icon />
    <span className="hidden sm:inline">Visible Label</span>
  </button>
  
  {/* Custom tooltip - appears on hover */}
  <span className="absolute z-50 -bottom-9 left-1/2 -translate-x-1/2 px-2 py-1 bg-gray-900 dark:bg-gray-700 text-white text-xs rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none">
    Tooltip text
    {/* Arrow pointing up */}
    <span className="absolute -top-1 left-1/2 -translate-x-1/2 w-2 h-2 bg-gray-900 dark:bg-gray-700 rotate-45" />
  </span>
</div>

Tooltip Position Variants

Position Classes Use Case
Below (default) -bottom-9 left-1/2 -translate-x-1/2 + arrow -top-1 Navigation buttons, icon-only buttons
Above -top-8 left-1/2 -translate-x-1/2 + arrow at bottom Buttons near bottom of viewport
Left-aligned -bottom-9 left-0 Buttons near right edge
Right-aligned -bottom-9 right-0 Buttons near left edge

Named Groups for Multiple Tooltips

When multiple tooltips exist in the same container, use named groups:

<div className="flex gap-2">
  <div className="relative group/vote">
    <button>Vote</button>
    <span className="... opacity-0 group-hover/vote:opacity-100">Upvote</span>
  </div>
  <div className="relative group/star">
    <button>Star</button>
    <span className="... opacity-0 group-hover/star:opacity-100">Add to favorites</span>
  </div>
</div>

Navigation Tooltip Specifics

For navigation items (buttons, links with icons):

  • Tooltip appears below the element
  • Include arrow pointing up to the button
  • Use aria-label for screen readers (instead of title)
  • Hide tooltip on mobile if label is visible
// NavLink with custom tooltip
function NavLink({ to, icon, label, tooltip }) {
  return (
    <div className="relative group">
      <Link to={to} aria-label={tooltip || label}>
        <span>{icon}</span>
        <span className="hidden sm:inline">{label}</span>
      </Link>
      {/* Only show tooltip when label is hidden (mobile) */}
      <span className="absolute z-50 -bottom-9 left-1/2 -translate-x-1/2 px-2 py-1 bg-gray-900 dark:bg-gray-700 text-white text-xs rounded whitespace-nowrap opacity-0 group-hover:opacity-100 sm:group-hover:opacity-0 transition-opacity pointer-events-none">
        {label}
        <span className="absolute -top-1 left-1/2 -translate-x-1/2 w-2 h-2 bg-gray-900 dark:bg-gray-700 rotate-45" />
      </span>
    </div>
  )
}

Card Action Button Pattern

For action buttons in cards:

<div className="relative group/[unique-name]">
  <button 
    className="flex items-center gap-1 px-2 py-1 rounded-md transition-all text-gray-500 hover:text-[accent-color] hover:bg-[accent-color]/10"
    aria-label="Action description"
  >
    <svg className="h-3.5 w-3.5" ... />
    <span className="font-medium">{count}</span>
  </button>
  <span className="absolute -top-8 left-1/2 -translate-x-1/2 px-2 py-1 bg-gray-900 dark:bg-gray-700 text-white text-xs rounded opacity-0 group-hover/[unique-name]:opacity-100 transition-opacity whitespace-nowrap pointer-events-none z-50 shadow-lg">
    Tooltip text
  </span>
</div>

Tooltip Styling Rules

  • Background: bg-gray-900 dark:bg-gray-700
  • Text: text-white text-xs
  • Padding: px-2 py-1
  • Border radius: rounded
  • Z-index: z-50
  • Transition: opacity-0 group-hover:opacity-100 transition-opacity
  • Arrow: w-2 h-2 rotate-45 matching background color
  • Pointer events: pointer-events-none (prevents tooltip from blocking clicks)
  • Parent containers: Must have overflow-visible (not overflow-hidden)

File Reference

  • Color definitions: tailwind.config.js
  • Theme context: src/contexts/ThemeContext.tsx
  • Theme toggle: src/components/ThemeToggle.tsx
  • Icon definitions: src/components/Navigation.tsx
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment