Skip to content

Good Deeds Design System

Technical implementation guide for the Good Deeds brand and UI components.

Overview

This design system provides developers and designers with the tools, components, and guidelines needed to build consistent, accessible, and high-quality Good Deeds experiences across all platforms.

Design System Principles

  1. Token-Based - All design decisions are codified as design tokens
  2. Component-Driven - Build with reusable, composable components
  3. Accessibility-First - WCAG AA compliance is built-in, not bolted on
  4. Platform-Aware - Respects platform conventions while maintaining brand consistency
  5. Developer-Friendly - Clear documentation, typed interfaces, easy integration

Design Tokens

Design tokens are the atomic design decisions that make up our visual language. They should be implemented as constants/variables in your codebase.

Color Tokens

typescript
// colors.ts
export const colors = {
  // Primary
  primary: {
    main: '#4CAF50',
    light: '#80E27E',
    dark: '#087F23',
    contrast: '#FFFFFF',
  },
  
  // Secondary
  secondary: {
    main: '#2196F3',
    light: '#6EC6FF',
    dark: '#0069C0',
    contrast: '#FFFFFF',
  },
  
  // Accent Colors
  accent: {
    warmth: '#FF9800',
    sunshine: '#FFC107',
    joy: '#9C27B0',
  },
  
  // Semantic
  success: '#4CAF50',
  warning: '#FF9800',
  error: '#F44336',
  info: '#2196F3',
  
  // Neutrals
  text: {
    primary: '#212121',      // 87% opacity equivalent
    secondary: '#757575',    // 60% opacity equivalent
    disabled: '#9E9E9E',     // 38% opacity equivalent
    hint: '#BDBDBD',         // 30% opacity equivalent
  },
  
  // Backgrounds
  background: {
    default: '#FFFFFF',
    paper: '#FFFFFF',
    surface: '#F5F5F5',
  },
  
  // Borders & Dividers
  divider: '#E0E0E0',
  border: '#E0E0E0',
  
  // Overlays
  overlay: {
    light: 'rgba(255, 255, 255, 0.9)',
    medium: 'rgba(0, 0, 0, 0.5)',
    dark: 'rgba(0, 0, 0, 0.7)',
  },
} as const;
css
/* colors.css - CSS Custom Properties */
:root {
  /* Primary */
  --color-primary: #4CAF50;
  --color-primary-light: #80E27E;
  --color-primary-dark: #087F23;
  --color-primary-contrast: #FFFFFF;
  
  /* Secondary */
  --color-secondary: #2196F3;
  --color-secondary-light: #6EC6FF;
  --color-secondary-dark: #0069C0;
  --color-secondary-contrast: #FFFFFF;
  
  /* Accent */
  --color-warmth: #FF9800;
  --color-sunshine: #FFC107;
  --color-joy: #9C27B0;
  
  /* Semantic */
  --color-success: #4CAF50;
  --color-warning: #FF9800;
  --color-error: #F44336;
  --color-info: #2196F3;
  
  /* Text */
  --color-text-primary: #212121;
  --color-text-secondary: #757575;
  --color-text-disabled: #9E9E9E;
  --color-text-hint: #BDBDBD;
  
  /* Backgrounds */
  --color-background: #FFFFFF;
  --color-surface: #F5F5F5;
  
  /* Borders */
  --color-divider: #E0E0E0;
  --color-border: #E0E0E0;
}

Typography Tokens

typescript
// typography.ts
export const typography = {
  fontFamily: {
    primary: '"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
    display: '"Poppins", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
    mono: '"JetBrains Mono", "Fira Code", "Courier New", monospace',
  },
  
  fontSize: {
    xs: '0.75rem',      // 12px
    sm: '0.875rem',     // 14px
    base: '1rem',       // 16px
    lg: '1.125rem',     // 18px
    xl: '1.25rem',      // 20px
    '2xl': '1.5rem',    // 24px
    '3xl': '1.75rem',   // 28px
    '4xl': '2.25rem',   // 36px
    '5xl': '3rem',      // 48px
  },
  
  fontWeight: {
    regular: 400,
    medium: 500,
    semibold: 600,
    bold: 700,
    extrabold: 800,
  },
  
  lineHeight: {
    tight: 1.1,
    snug: 1.2,
    normal: 1.5,
    relaxed: 1.6,
  },
  
  letterSpacing: {
    tighter: '-0.02em',
    tight: '-0.01em',
    normal: '0',
    wide: '0.01em',
    wider: '0.02em',
  },
} as const;

// Predefined text styles
export const textStyles = {
  hero: {
    fontFamily: typography.fontFamily.display,
    fontSize: typography.fontSize['5xl'],
    fontWeight: typography.fontWeight.extrabold,
    lineHeight: typography.lineHeight.tight,
    letterSpacing: typography.letterSpacing.tighter,
  },
  h1: {
    fontFamily: typography.fontFamily.display,
    fontSize: typography.fontSize['4xl'],
    fontWeight: typography.fontWeight.bold,
    lineHeight: typography.lineHeight.snug,
    letterSpacing: typography.letterSpacing.tight,
  },
  h2: {
    fontFamily: typography.fontFamily.display,
    fontSize: typography.fontSize['3xl'],
    fontWeight: typography.fontWeight.bold,
    lineHeight: typography.lineHeight.snug,
    letterSpacing: typography.letterSpacing.tight,
  },
  h3: {
    fontFamily: typography.fontFamily.display,
    fontSize: typography.fontSize['2xl'],
    fontWeight: typography.fontWeight.semibold,
    lineHeight: typography.lineHeight.snug,
  },
  h4: {
    fontFamily: typography.fontFamily.primary,
    fontSize: typography.fontSize.xl,
    fontWeight: typography.fontWeight.semibold,
    lineHeight: typography.lineHeight.normal,
  },
  bodyLarge: {
    fontFamily: typography.fontFamily.primary,
    fontSize: typography.fontSize.lg,
    fontWeight: typography.fontWeight.regular,
    lineHeight: typography.lineHeight.relaxed,
  },
  body: {
    fontFamily: typography.fontFamily.primary,
    fontSize: typography.fontSize.base,
    fontWeight: typography.fontWeight.regular,
    lineHeight: typography.lineHeight.relaxed,
  },
  bodySmall: {
    fontFamily: typography.fontFamily.primary,
    fontSize: typography.fontSize.sm,
    fontWeight: typography.fontWeight.regular,
    lineHeight: typography.lineHeight.normal,
  },
  caption: {
    fontFamily: typography.fontFamily.primary,
    fontSize: typography.fontSize.xs,
    fontWeight: typography.fontWeight.medium,
    lineHeight: typography.lineHeight.normal,
    letterSpacing: typography.letterSpacing.wide,
  },
  button: {
    fontFamily: typography.fontFamily.primary,
    fontSize: typography.fontSize.base,
    fontWeight: typography.fontWeight.semibold,
    lineHeight: typography.lineHeight.normal,
    letterSpacing: typography.letterSpacing.wider,
  },
} as const;

Spacing Tokens

typescript
// spacing.ts
export const spacing = {
  0: '0',
  1: '0.25rem',   // 4px
  2: '0.5rem',    // 8px
  3: '0.75rem',   // 12px
  4: '1rem',      // 16px
  5: '1.25rem',   // 20px
  6: '1.5rem',    // 24px
  8: '2rem',      // 32px
  10: '2.5rem',   // 40px
  12: '3rem',     // 48px
  16: '4rem',     // 64px
  20: '5rem',     // 80px
  24: '6rem',     // 96px
} as const;

Border Radius Tokens

typescript
// borderRadius.ts
export const borderRadius = {
  none: '0',
  sm: '4px',
  md: '8px',
  lg: '16px',
  xl: '24px',
  full: '9999px',
} as const;

Shadow Tokens

typescript
// shadows.ts
export const shadows = {
  none: 'none',
  sm: '0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24)',
  md: '0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23)',
  lg: '0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23)',
  xl: '0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22)',
} as const;

Animation Tokens

typescript
// animation.ts
export const animation = {
  duration: {
    instant: '100ms',
    fast: '200ms',
    normal: '300ms',
    slow: '500ms',
  },
  
  easing: {
    linear: 'linear',
    easeIn: 'cubic-bezier(0.4, 0, 1, 1)',
    easeOut: 'cubic-bezier(0, 0, 0.2, 1)',
    easeInOut: 'cubic-bezier(0.4, 0, 0.2, 1)',
    bounce: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)',
  },
} as const;

Breakpoint Tokens

typescript
// breakpoints.ts
export const breakpoints = {
  mobile: '0px',
  tablet: '640px',
  desktop: '1024px',
  wide: '1440px',
} as const;

Component Library

Button Component

Variants

Primary Button

typescript
interface ButtonProps {
  variant?: 'primary' | 'secondary' | 'tertiary' | 'danger';
  size?: 'small' | 'medium' | 'large';
  disabled?: boolean;
  loading?: boolean;
  icon?: ReactNode;
  iconPosition?: 'left' | 'right';
  fullWidth?: boolean;
}

Specifications:

css
/* Primary Button */
.button-primary {
  background-color: var(--color-primary);
  color: var(--color-primary-contrast);
  border: none;
  border-radius: 4px;
  padding: 12px 24px;
  font-family: var(--font-primary);
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.5;
  letter-spacing: 0.02em;
  min-height: 44px;
  cursor: pointer;
  transition: all 200ms ease-out;
}

.button-primary:hover {
  background-color: var(--color-primary-dark);
  box-shadow: var(--shadow-md);
}

.button-primary:active {
  background-color: var(--color-primary-dark);
  box-shadow: var(--shadow-sm);
  transform: translateY(1px);
}

.button-primary:disabled {
  background-color: var(--color-text-disabled);
  cursor: not-allowed;
  box-shadow: none;
}

/* Secondary Button */
.button-secondary {
  background-color: transparent;
  color: var(--color-primary);
  border: 1px solid var(--color-primary);
  border-radius: 4px;
  padding: 12px 24px;
  min-height: 44px;
}

.button-secondary:hover {
  background-color: rgba(76, 175, 80, 0.08);
}

/* Tertiary Button */
.button-tertiary {
  background-color: transparent;
  color: var(--color-primary);
  border: none;
  padding: 12px 16px;
  min-height: 44px;
}

.button-tertiary:hover {
  background-color: rgba(76, 175, 80, 0.08);
}

/* Size Variants */
.button-small {
  padding: 8px 16px;
  font-size: 0.875rem;
  min-height: 36px;
}

.button-large {
  padding: 16px 32px;
  font-size: 1.125rem;
  min-height: 52px;
}

Usage Examples

tsx
// React/React Native
<Button variant="primary" size="medium">
  Help Now
</Button>

<Button variant="secondary" icon={<AddIcon />} iconPosition="left">
  Create Request
</Button>

<Button variant="primary" loading>
  Submitting...
</Button>

Input Components

Text Input

typescript
interface TextInputProps {
  label: string;
  value: string;
  onChange: (value: string) => void;
  placeholder?: string;
  helperText?: string;
  error?: string;
  disabled?: boolean;
  required?: boolean;
  type?: 'text' | 'email' | 'password' | 'tel' | 'number';
  startIcon?: ReactNode;
  endIcon?: ReactNode;
}

Specifications:

css
.input-wrapper {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.input-label {
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--color-text-primary);
  margin-bottom: 4px;
}

.input-label-required::after {
  content: ' *';
  color: var(--color-error);
}

.input-field {
  width: 100%;
  min-height: 48px;
  padding: 12px 16px;
  font-size: 1rem;
  font-family: var(--font-primary);
  color: var(--color-text-primary);
  background-color: var(--color-background);
  border: 1px solid var(--color-border);
  border-radius: 4px;
  transition: all 200ms ease-out;
}

.input-field:focus {
  outline: none;
  border: 2px solid var(--color-primary);
  padding: 11px 15px; /* Adjust for thicker border */
}

.input-field.error {
  border: 2px solid var(--color-error);
}

.input-field:disabled {
  background-color: var(--color-surface);
  color: var(--color-text-disabled);
  cursor: not-allowed;
}

.input-helper-text {
  font-size: 0.75rem;
  color: var(--color-text-secondary);
  margin-top: 4px;
}

.input-error-text {
  font-size: 0.75rem;
  color: var(--color-error);
  margin-top: 4px;
}

Card Component

typescript
interface CardProps {
  elevation?: 'none' | 'sm' | 'md' | 'lg';
  padding?: keyof typeof spacing;
  radius?: keyof typeof borderRadius;
  children: ReactNode;
  onClick?: () => void;
  hoverable?: boolean;
}

Specifications:

css
.card {
  background-color: var(--color-background);
  border-radius: 8px;
  padding: 16px;
  box-shadow: var(--shadow-sm);
  transition: all 200ms ease-out;
}

.card-hoverable:hover {
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
  cursor: pointer;
}

.card-elevation-none {
  box-shadow: none;
}

.card-elevation-sm {
  box-shadow: var(--shadow-sm);
}

.card-elevation-md {
  box-shadow: var(--shadow-md);
}

.card-elevation-lg {
  box-shadow: var(--shadow-lg);
}

Badge/Chip Component

typescript
interface ChipProps {
  label: string;
  color?: 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'neutral';
  size?: 'small' | 'medium';
  icon?: ReactNode;
  onDelete?: () => void;
  clickable?: boolean;
}

Specifications:

css
.chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 12px;
  border-radius: 16px;
  font-size: 0.75rem;
  font-weight: 500;
  white-space: nowrap;
  transition: all 200ms ease-out;
}

.chip-primary {
  background-color: rgba(76, 175, 80, 0.12);
  color: var(--color-primary-dark);
}

.chip-small {
  padding: 2px 8px;
  font-size: 0.625rem;
}

.chip-clickable:hover {
  opacity: 0.8;
  cursor: pointer;
}

Avatar Component

typescript
interface AvatarProps {
  src?: string;
  alt: string;
  size?: 'small' | 'medium' | 'large' | 'xlarge';
  fallback?: string; // Initials
  online?: boolean;
}

Specifications:

css
.avatar {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  overflow: hidden;
  background-color: var(--color-primary);
  color: white;
  font-weight: 600;
}

.avatar-small {
  width: 32px;
  height: 32px;
  font-size: 0.75rem;
}

.avatar-medium {
  width: 48px;
  height: 48px;
  font-size: 1rem;
}

.avatar-large {
  width: 64px;
  height: 64px;
  font-size: 1.25rem;
}

.avatar-xlarge {
  width: 96px;
  height: 96px;
  font-size: 1.75rem;
}

.avatar-online-indicator {
  position: absolute;
  bottom: 0;
  right: 0;
  width: 25%;
  height: 25%;
  border-radius: 50%;
  background-color: var(--color-success);
  border: 2px solid var(--color-background);
}

Layout Patterns

Mobile App Layout

┌─────────────────────────┐
│     Top App Bar         │  Height: 56px
├─────────────────────────┤
│                         │
│                         │
│   Main Content Area     │  Scrollable
│                         │
│                         │
├─────────────────────────┤
│  Bottom Navigation      │  Height: 56px
└─────────────────────────┘

Top App Bar:

  • Height: 56px
  • Padding: 16px horizontal
  • Logo/Title on left
  • Actions on right

Content Area:

  • Padding: 16px all sides
  • Safe area insets respected
  • Max width for readability on tablets

Bottom Navigation:

  • Height: 56px
  • 3-5 primary actions
  • Icons + labels
  • Active state: Primary green

Spacing System

Use consistent spacing throughout:

Extra Small:  4px   (spacing-1)
Small:        8px   (spacing-2)
Medium:       16px  (spacing-4)
Large:        24px  (spacing-6)
Extra Large:  32px  (spacing-8)

Card/Section Spacing:

  • Internal padding: 16px
  • Gap between elements: 12px
  • Gap between sections: 24px

Accessibility Implementation

Focus Indicators

css
/* Default focus ring */
*:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
  border-radius: 2px;
}

/* Custom focus for specific components */
.button:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

ARIA Patterns

Button:

tsx
<button
  aria-label="Help your neighbor Maria"
  aria-disabled={disabled}
  aria-busy={loading}
>
  {children}
</button>

Input:

tsx
<div>
  <label htmlFor="task-description">
    Task Description
  </label>
  <input
    id="task-description"
    aria-describedby="task-description-hint"
    aria-invalid={hasError}
    aria-required={required}
  />
  <span id="task-description-hint">
    Describe what help you need
  </span>
</div>

Screen Reader Text

css
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

Platform-Specific Considerations

iOS

typescript
// Use iOS native components where appropriate
import { 
  SafeAreaView, 
  StatusBar,
  Platform
} from 'react-native';

// Respect iOS safe area
<SafeAreaView style={styles.container}>
  <StatusBar barStyle="dark-content" />
  {content}
</SafeAreaView>

// iOS-specific styling
const styles = StyleSheet.create({
  button: {
    ...Platform.select({
      ios: {
        shadowColor: '#000',
        shadowOffset: { width: 0, height: 2 },
        shadowOpacity: 0.1,
        shadowRadius: 4,
      },
      android: {
        elevation: 2,
      },
    }),
  },
});

Android

typescript
// Material Design 3 compliance
import { MD3LightTheme, MD3DarkTheme } from 'react-native-paper';

const theme = {
  ...MD3LightTheme,
  colors: {
    ...MD3LightTheme.colors,
    primary: '#4CAF50',
    secondary: '#2196F3',
    // ... other Good Deeds colors
  },
};

Web

css
/* Responsive design */
@media (max-width: 640px) {
  .container {
    padding: 16px;
  }
}

@media (min-width: 641px) and (max-width: 1024px) {
  .container {
    padding: 24px;
    max-width: 768px;
    margin: 0 auto;
  }
}

@media (min-width: 1025px) {
  .container {
    padding: 32px;
    max-width: 1200px;
    margin: 0 auto;
  }
}

Animation Guidelines

Micro-interactions

css
/* Button press feedback */
.button {
  transition: all 200ms cubic-bezier(0, 0, 0.2, 1);
}

.button:active {
  transform: scale(0.98);
}

/* Loading states */
@keyframes spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

.loading-spinner {
  animation: spin 1s linear infinite;
}

/* Fade in */
@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.fade-in {
  animation: fadeIn 300ms ease-out;
}

/* Slide up */
@keyframes slideUp {
  from {
    transform: translateY(100%);
    opacity: 0;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}

.slide-up {
  animation: slideUp 300ms ease-out;
}

Respect Reduced Motion

css
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

Implementation Checklist

When implementing a new feature or component:

Design

  • [ ] Follows brand guidelines
  • [ ] Uses design tokens
  • [ ] Responsive across breakpoints
  • [ ] Dark mode considered (if applicable)

Accessibility

  • [ ] WCAG AA compliant color contrast
  • [ ] Keyboard navigable
  • [ ] Screen reader friendly
  • [ ] Focus indicators visible
  • [ ] Touch targets ≥ 44x44px
  • [ ] Reduced motion respected

Code Quality

  • [ ] Type-safe (TypeScript)
  • [ ] Properly documented
  • [ ] Unit tests included
  • [ ] Performance optimized
  • [ ] Error states handled

Platform

  • [ ] Works on iOS
  • [ ] Works on Android
  • [ ] Works on Web (if applicable)
  • [ ] Platform-specific patterns respected

Tools & Resources

Design Tools

  • Figma: Component library and design files
  • Storybook: Component documentation and testing
  • Chromatic: Visual regression testing

Development Tools

  • TypeScript: Type safety
  • ESLint: Code linting
  • Prettier: Code formatting
  • Jest: Unit testing
  • Axe: Accessibility testing

Fonts

Icons


Version Control

Current Version: 1.0.0

Changelog:

  • v1.0.0 (June 2026): Initial design system release

Support

For questions or contributions to the design system:

  • Internal: Design System Team
  • GitHub: [Repository Issues]
  • Slack: #design-system

This design system is maintained by the Good Deeds design and engineering teams. Contributions and feedback are welcome.

Good Deeds - Nachbarschaftshilfe-App