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