Things 3: Die Kunst der fokussierten Einfachheit
Wie Things 3 Einfachheit durch Einschränkungen erreichte: natürliche Hierarchie, When vs Deadline Trennung, Tastatur-first Design und bedeutungsvolle Farbzurückhaltung. Mit SwiftUI-Implementierungsmustern.
Things 3: Die Kunst der fokussierten Einfachheit
„Wir glauben, dass einfach schwer ist." — Cultured Code
Things 3 wird oft als der schönste Task-Manager bezeichnet, der je entwickelt wurde. Diese Schönheit entsteht durch kompromisslosen Fokus auf das Wesentliche und die Eliminierung von allem anderen.
Warum Things 3 wichtig ist
Things 3 zeigt, dass Einschränkungen Klarheit schaffen. Während Konkurrenten Features anhäufen (wiederkehrende Aufgaben mit 47 Optionen, Projekte mit Abhängigkeiten und Gantt-Diagrammen), fragt Things: Was braucht ein Mensch wirklich, um Dinge erledigt zu bekommen?
Zentrale Errungenschaften: - Visuelle Einfachheit ohne Einbußen bei der Tiefe erreicht - Datumsangaben bedeutungsvoll gemacht (Today, Evening, Someday) - Eine Hierarchie geschaffen, die widerspiegelt, wie Menschen über Arbeit denken - Bewiesen, dass Tastatur-orientiert auch schön sein kann - Den Standard für natives macOS/iOS-App-Design gesetzt
Zentrale Erkenntnisse
- Trenne „Wann werde ich das tun?" von „Wann ist es fällig?" - Things' Dual-Date-Modell (When + Deadline) beseitigt die Mehrdeutigkeit, unter der die meisten Task-Apps leiden; geplante Absicht und harte Deadlines dienen unterschiedlichen Zwecken
- Someday ist ein Feature, kein Versagenszustand - Ideen einen druckfreien Ort zu geben, verhindert Inbox-Angst; Aufgaben in Someday überladen nicht Today und lösen keine Schuldgefühle aus
- Areas werden nie abgeschlossen, Projects schon - Diese Unterscheidung spiegelt menschliche Kognition wider: „Arbeit" ist ein fortlaufender Lebensbereich, „Ship v2.0" hat eine Ziellinie; diese zu vermischen erzeugt Verwirrung
- Reserviere Farbe für Bedeutung - Things' Oberfläche ist zu 95% neutral (Schwarz, Weiß, Grau); Farbe erscheint nur für semantische Information (Gelb=Today, Rot=Deadline, Indigo=Evening)
- Erledigung sollte sich belohnend anfühlen - Die Checkbox-Animation (Füllen → Häkchen → Sound) dauert 500ms, liefert aber Dopamin; Aufgaben zu erledigen wird intrinsisch motivierend
Grundlegende Design-Prinzipien
1. Die natürliche Hierarchie
Things organisiert Arbeit so, wie Menschen darüber denken: vom Breiten (Lebensbereiche) zum Spezifischen (einzelne Aufgaben).
THINGS HIERARCHY:
Area (Work, Personal, Health) ← Broad life categories
│
└── Project (Launch App) ← Finite goal with tasks
│
└── Heading (Design) ← Optional grouping
│
└── To-Do ← Single actionable item
│
└── Checklist ← Sub-steps within task
Example:
┌─────────────────────────────────────────────────────────────┐
│ [A] WORK (Area) │
│ ├── [P] Ship Version 2.0 (Project) │
│ │ ├── [H] Design │
│ │ │ ├── [ ] Create new icons │
│ │ │ └── [ ] Update color palette │
│ │ └── [H] Development │
│ │ ├── [ ] Implement auth flow │
│ │ └── [ ] Add analytics │
│ └── [P] Website Redesign (Project) │
│ └── [ ] Write copy for landing page │
└─────────────────────────────────────────────────────────────┘
Die zentrale Erkenntnis: Areas werden nie abgeschlossen (sie sind Lebenskategorien). Projects werden abgeschlossen (bis Freitag ausliefern). Diese Unterscheidung eliminiert Planungsparalyse.
Datenmodell:
// Things' hierarchy expressed in code
struct Area: Identifiable {
let id: UUID
var title: String
var projects: [Project]
var tasks: [Task] // Loose tasks not in projects
}
struct Project: Identifiable {
let id: UUID
var title: String
var notes: String?
var deadline: Date?
var headings: [Heading]
var tasks: [Task]
var isComplete: Bool
}
struct Heading: Identifiable {
let id: UUID
var title: String
var tasks: [Task]
}
struct Task: Identifiable {
let id: UUID
var title: String
var notes: String?
var when: TaskSchedule?
var deadline: Date?
var tags: [Tag]
var checklist: [ChecklistItem]
var isComplete: Bool
}
enum TaskSchedule {
case today
case evening
case specificDate(Date)
case someday
}
2. When, nicht nur Due
Things trennt „Wann werde ich das tun?" (Today, Evening, Someday) von „Wann muss das erledigt sein?" (Deadline). Die meisten Apps vermischen diese Konzepte.
TRADITIONAL DATE MODEL:
┌──────────────────────────────────────────────────────────────┐
│ Task: Write report │
│ Due: March 15th │
│ │
│ Problem: Is March 15th when I'll do it, or when it's due? │
│ If it's due, when should I start? │
└──────────────────────────────────────────────────────────────┘
THINGS DATE MODEL:
┌──────────────────────────────────────────────────────────────┐
│ Task: Write report │
│ When: Today (I'll work on it today) │
│ Deadline: March 15th (must be complete by then) │
│ │
│ Clarity: Two separate questions, two separate answers │
└──────────────────────────────────────────────────────────────┘
SMART LISTS (automatic filtering):
┌───────────────────┬─────────────────────────────────────────┐
│ [I] INBOX │ Uncategorized captures │
├───────────────────┼─────────────────────────────────────────┤
│ [*] TODAY │ when == .today OR deadline == today │
├───────────────────┼─────────────────────────────────────────┤
│ [E] THIS EVENING │ when == .evening │
├───────────────────┼─────────────────────────────────────────┤
│ [D] UPCOMING │ when == .specificDate (future) │
├───────────────────┼─────────────────────────────────────────┤
│ [A] ANYTIME │ when == nil AND deadline == nil │
├───────────────────┼─────────────────────────────────────────┤
│ [S] SOMEDAY │ when == .someday │
├───────────────────┼─────────────────────────────────────────┤
│ [L] LOGBOOK │ isComplete == true │
└───────────────────┴─────────────────────────────────────────┘
Die zentrale Erkenntnis: „Someday" ist kein Versagenszustand – es ist ein Druckventil. Ideen haben einen Platz, ohne Today zu überladen.
SwiftUI-Implementierung:
struct WhenPicker: View {
@Binding var schedule: TaskSchedule?
@Binding var deadline: Date?
@State private var showDatePicker = false
var body: some View {
VStack(spacing: 12) {
// Quick options
HStack(spacing: 8) {
WhenButton(
icon: "star.fill",
label: "Today",
color: .yellow,
isSelected: schedule == .today
) {
schedule = .today
}
WhenButton(
icon: "moon.fill",
label: "Evening",
color: .indigo,
isSelected: schedule == .evening
) {
schedule = .evening
}
WhenButton(
icon: "calendar",
label: "Pick Date",
color: .red,
isSelected: showDatePicker
) {
showDatePicker = true
}
WhenButton(
icon: "archivebox.fill",
label: "Someday",
color: .brown,
isSelected: schedule == .someday
) {
schedule = .someday
}
}
// Deadline (separate from "when")
if let deadline = deadline {
HStack {
Image(systemName: "flag.fill")
.foregroundStyle(.red)
Text("Deadline: \(deadline, style: .date)")
Spacer()
Button("Clear") { self.deadline = nil }
}
.font(.caption)
}
}
}
}
struct WhenButton: View {
let icon: String
let label: String
let color: Color
let isSelected: Bool
let action: () -> Void
var body: some View {
Button(action: action) {
VStack(spacing: 4) {
Image(systemName: icon)
.font(.title2)
Text(label)
.font(.caption2)
}
.frame(maxWidth: .infinity)
.padding(.vertical, 8)
.background(isSelected ? color.opacity(0.2) : .clear)
.cornerRadius(8)
}
.buttonStyle(.plain)
.foregroundStyle(isSelected ? color : .secondary)
}
}
3. Tastatur-zuerst, Maus-freundlich
Alles lässt sich vollständig über die Tastatur mit Muskelgedächtnis-Effizienz bedienen, bleibt aber gleichzeitig intuitiv für Mausbenutzer.
TASTATURKÜRZEL (einprägsame Muster):
NAVIGATION:
Cmd+1-6 Zu Smart-Listen springen (Inbox, Heute, etc.)
Cmd+Up/Down Zwischen Abschnitten wechseln
Space Schnellansicht (Aufgabendetails anzeigen)
AUFGABENBEARBEITUNG:
Cmd+K Aufgabe abschließen
Cmd+S Planen (Wann-Auswahl)
Cmd+Shift+D Deadline setzen
Cmd+Shift+M In Projekt verschieben
Cmd+Shift+T Tags hinzufügen
ERSTELLEN:
Space/Return Neue Aufgabe erstellen (kontextabhängig)
Cmd+N Neue Aufgabe in Inbox
Cmd+Opt+N Neues Projekt
Schnelleingabe (globaler Hotkey):
Ctrl+Space Öffnet schwebendes Schnelleingabefeld von überall
┌─────────────────────────────────────────────────┐
│ [ ] | │
│ Neue Aufgabe erscheint, wo immer du bist │
└─────────────────────────────────────────────────┘
Die zentrale Erkenntnis: Tastaturkürzel sollten entdeckbar, aber nicht erforderlich sein. Die Oberfläche deutet Tasten an, ohne sie zu verlangen.
CSS-Pattern (Shortcut-Hinweise):
.action-button {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
}
.shortcut-hint {
font-size: 11px;
font-family: var(--font-mono);
padding: 2px 6px;
background: var(--surface-subtle);
border-radius: 4px;
color: var(--text-tertiary);
opacity: 0;
transition: opacity 0.15s ease;
}
/* Bei Hover anzeigen */
.action-button:hover .shortcut-hint {
opacity: 1;
}
/* Immer sichtbar bei Tastatur-Fokus */
.action-button:focus-visible .shortcut-hint {
opacity: 1;
}
SwiftUI Schnelleingabe:
struct QuickEntryPanel: View {
@Binding var isPresented: Bool
@State private var taskTitle = ""
@FocusState private var isFocused: Bool
var body: some View {
VStack(spacing: 0) {
HStack(spacing: 12) {
// Checkbox (nur visuell)
Circle()
.strokeBorder(.tertiary, lineWidth: 1.5)
.frame(width: 20, height: 20)
// Task input
TextField("New To-Do", text: $taskTitle)
.textFieldStyle(.plain)
.font(.body)
.focused($isFocused)
.onSubmit {
createTask()
}
}
.padding()
.background(.ultraThinMaterial)
.cornerRadius(12)
.shadow(color: .black.opacity(0.2), radius: 20, y: 10)
}
.frame(width: 500)
.onAppear {
isFocused = true
}
.onExitCommand {
isPresented = false
}
}
private func createTask() {
guard !taskTitle.isEmpty else { return }
// Create task in Inbox
TaskStore.shared.createTask(title: taskTitle)
taskTitle = ""
}
}
4. Visuelle Zurückhaltung
Things setzt Farbe sparsam ein und reserviert sie ausschließlich für bedeutungsvolle Elemente. Der Großteil der Oberfläche ist neutral gehalten, sodass farbige Elemente hervorstechen.
COLOR USAGE IN THINGS:
Neutral (95% of interface):
├── Background: Pure white / Deep gray
├── Text: Black / White
├── Borders: Very subtle gray
└── Icons: Monochrome
Meaningful Color (5%):
├── Yellow [*] = Today
├── Red [!] = Deadline / Overdue
├── Blue [#] = Tags (user-assigned)
├── Green [x] = Completion animation
└── Indigo [E] = Evening
VISUAL EXAMPLE:
┌─────────────────────────────────────────────────────────────┐
│ TODAY [*] (yellow) │
├─────────────────────────────────────────────────────────────┤
│ ( ) Write documentation │
│ Project Name - [!] Due tomorrow (red) │
│ │
│ ( ) Review pull requests │
│ [#] work (blue tag) │
│ │
│ ( ) Call dentist │
│ [E] This Evening (indigo badge) │
└─────────────────────────────────────────────────────────────┘
Notice: Most text is black. Color only appears where it carries meaning.
Die zentrale Erkenntnis: Wenn alles bunt ist, sticht nichts hervor. Reserviere Farbe für Information.
CSS-Pattern:
:root {
/* Neutral palette (most of UI) */
--surface-primary: #ffffff;
--surface-secondary: #f5f5f7;
--text-primary: #1d1d1f;
--text-secondary: #86868b;
--border: rgba(0, 0, 0, 0.08);
/* Semantic colors (used sparingly) */
--color-today: #f5c518;
--color-deadline: #ff3b30;
--color-evening: #5856d6;
--color-complete: #34c759;
--color-tag: #007aff;
}
/* Task row - mostly neutral */
.task-row {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 12px 16px;
border-bottom: 1px solid var(--border);
background: var(--surface-primary);
color: var(--text-primary);
}
/* Color only for meaning */
.deadline-badge {
font-size: 12px;
color: var(--color-deadline);
}
.deadline-badge.overdue {
font-weight: 600;
color: var(--color-deadline);
}
.tag {
font-size: 12px;
padding: 2px 8px;
border-radius: 4px;
background: var(--color-tag);
color: white;
}
.evening-badge {
color: var(--color-evening);
}
5. Befriedigende Erledigung
Die Erledigungsanimation liefert Belohnung, nicht bloßes Feedback. Ein zufriedenstellendes „Plink"-Geräusch und eine flüssige Häkchen-Animation machen das Abschließen von Aufgaben zu einem guten Gefühl.
COMPLETION SEQUENCE:
Frame 1: ○ Task title
Frame 2-4: ◔ (circle fills clockwise)
Frame 5-7: ● (fully filled, brief pause)
Frame 8-10: ✓ (check appears, scales up slightly)
Frame 11+: Row slides away, list closes gap
Audio: Soft "plink" at Frame 7-8 (completion moment)
Haptic: Light tap on iOS at completion
SwiftUI-Implementierung:
struct CompletableCheckbox: View {
@Binding var isComplete: Bool
@State private var animationPhase: AnimationPhase = .unchecked
enum AnimationPhase {
case unchecked, filling, checked, done
}
var body: some View {
Button {
completeWithAnimation()
} label: {
ZStack {
// Background circle
Circle()
.strokeBorder(.tertiary, lineWidth: 1.5)
.frame(width: 22, height: 22)
```swift
// Fill animation
Circle()
.trim(from: 0, to: fillAmount)
.stroke(.green, lineWidth: 1.5)
.frame(width: 22, height: 22)
.rotationEffect(.degrees(-90))
// Checkmark
Image(systemName: "checkmark")
.font(.system(size: 12, weight: .bold))
.foregroundStyle(.green)
.scaleEffect(checkScale)
.opacity(checkOpacity)
}
}
.buttonStyle(.plain)
.sensoryFeedback(.success, trigger: animationPhase == .checked)
}
private var fillAmount: CGFloat {
switch animationPhase {
case .unchecked: return 0
case .filling: return 1
case .checked, .done: return 1
}
}
private var checkScale: CGFloat {
animationPhase == .checked ? 1.2 : (animationPhase == .done ? 1.0 : 0)
}
private var checkOpacity: CGFloat {
animationPhase == .unchecked || animationPhase == .filling ? 0 : 1
}
private func completeWithAnimation() {
// Phase 1: Fill the circle
withAnimation(.easeInOut(duration: 0.2)) {
animationPhase = .filling
}
// Phase 2: Show checkmark with bounce
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
withAnimation(.spring(response: 0.3, dampingFraction: 0.5)) {
animationPhase = .checked
}
}
// Phase 3: Settle and mark complete
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
withAnimation(.easeOut(duration: 0.15)) {
animationPhase = .done
}
isComplete = true
}
}
}
Übertragbare Muster
Muster 1: Progressive Aufgabendetails
Aufgabendetails werden inline erweitert, anstatt zu einer separaten Ansicht zu navigieren.
struct ExpandableTask: View {
let task: Task
@State private var isExpanded = false
var body: some View {
VStack(alignment: .leading, spacing: 0) {
// Always visible row
TaskRow(task: task, isExpanded: $isExpanded)
// Expandable details
if isExpanded {
TaskDetails(task: task)
.padding(.leading, 44) // Align with title
.transition(.asymmetric(
insertion: .push(from: .top).combined(with: .opacity),
removal: .push(from: .bottom).combined(with: .opacity)
))
}
}
.animation(.spring(response: 0.3, dampingFraction: 0.8), value: isExpanded)
}
}
Muster 2: Natürlichsprachliche Eingabe
Things analysiert natürliche Sprache zur Datumsangabe: „tomorrow", „next week", „June 15".
function parseNaturalDate(input: string): TaskSchedule | null {
const lower = input.toLowerCase();
// Today shortcuts
if (['today', 'tod', 'now'].includes(lower)) {
return { type: 'today' };
}
// Evening
if (['tonight', 'evening', 'eve'].includes(lower)) {
return { type: 'evening' };
}
// Tomorrow
if (['tomorrow', 'tom', 'tmrw'].includes(lower)) {
const date = new Date();
date.setDate(date.getDate() + 1);
return { type: 'date', date };
}
// Someday
if (['someday', 'later', 'eventually'].includes(lower)) {
return { type: 'someday' };
}
// Relative days
const inMatch = lower.match(/in (\d+) days?/);
if (inMatch) {
const date = new Date();
date.setDate(date.getDate() + parseInt(inMatch[1]));
return { type: 'date', date };
}
// Day names
const days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
const dayIndex = days.findIndex(d => lower.startsWith(d.slice(0, 3)));
if (dayIndex !== -1) {
const date = getNextDayOfWeek(dayIndex);
return { type: 'date', date };
}
return null;
}
Pattern 3: Listenbasiertes Drag-and-Drop
Das Neuordnen erfolgt durch direkte Manipulation mit klarem visuellen Feedback.
.task-row {
transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.task-row.dragging {
transform: scale(1.02);
box-shadow:
0 4px 12px rgba(0, 0, 0, 0.15),
0 0 0 2px var(--color-focus);
z-index: 100;
}
.task-row.drop-above::before {
content: '';
position: absolute;
top: -2px;
left: 44px;
right: 16px;
height: 4px;
background: var(--color-focus);
border-radius: 2px;
}
.task-row.drop-below::after {
content: '';
position: absolute;
bottom: -2px;
left: 44px;
right: 16px;
height: 4px;
background: var(--color-focus);
border-radius: 2px;
}
Design-Lektionen
- Belange trennen: „Wann werde ich das tun?" vs. „Wann ist es fällig?" sind unterschiedliche Fragen
- Zurückhaltung bei Farbe: Farbe für Bedeutung reservieren; Neutral ist der Standard
- Abschluss ist Belohnung: Das Erledigen von Aufgaben soll sich gut anfühlen
- Tastatur beschleunigt, Maus heißt willkommen: Für beide gestalten, ohne Kompromisse bei einem zu machen
- Natürliche Hierarchien: Widerspiegeln, wie Menschen bereits über Arbeit denken (Bereiche → Projekte → Aufgaben)
- Irgendwann ist ein Feature: Ideen ein Zuhause geben, das keinen Druck erzeugt
Häufig gestellte Fragen
Was ist der Unterschied zwischen „Wann" und „Frist" in Things 3?
„Wann" beantwortet „wann werde ich daran arbeiten?" (Heute, Heute Abend, Irgendwann oder ein bestimmtes Datum). „Frist" beantwortet „wann muss dies fertig sein?" Die meisten Aufgaben-Apps vermischen diese, aber sie dienen unterschiedlichen Zwecken. Eine Aufgabe könnte am Freitag fällig sein (Frist), aber du planst, am Mittwoch daran zu arbeiten (Wann). Diese Trennung ermöglicht es dir, deinen Tag nach Absichten zu planen und gleichzeitig harte Fälligkeitstermine zu verfolgen.
Wie funktioniert die Hierarchie von Things 3 (Bereiche, Projekte, Aufgaben)?
Bereiche sind breite Lebenskategorien, die nie abgeschlossen werden (Arbeit, Persönlich, Gesundheit). Projekte sind endliche Ziele mit einem klaren Endzustand (App veröffentlichen, Urlaub planen). Aufgaben sind einzelne umsetzbare Punkte. Überschriften gruppieren optional Aufgaben innerhalb von Projekten. Dies spiegelt wider, wie Menschen natürlich über Arbeit denken: Laufende Verantwortlichkeiten enthalten zeitgebundene Ziele, die diskrete Aktionen enthalten.
Warum verwendet Things 3 so wenig Farbe?
Things reserviert Farbe ausschließlich für semantische Bedeutung. Die Benutzeroberfläche ist zu 95% neutral (Schwarz, Weiß, Grau), sodass Farbe, wenn sie erscheint, kommuniziert: Gelb bedeutet Heute, Rot bedeutet Frist/Überfällig, Indigo bedeutet Abend, Blau markiert Tags. Wenn alles bunt wäre, würde nichts hervorstechen. Die Zurückhaltung macht die bedeutungsvollen Farben unmöglich zu übersehen.
Was ist „Irgendwann" in Things 3 und warum ist es wertvoll?
Irgendwann ist ein dedizierter Bereich für Aufgaben, die du dir merken möchtest, die aber nicht deine aktiven Listen überladen sollen. Es ist kein Versagenszustand – es ist ein Druckentlastungsventil. Ideen für „irgendwann" (Spanisch lernen, dieses Buch lesen) haben ein Zuhause, ohne tägliche Schuldgefühle zu erzeugen. Du kannst Irgendwann wöchentlich durchgehen und Aufgaben zu Heute befördern, wenn du bereit bist.
Wie funktioniert die Abschluss-Animation von Things 3?
Wenn du auf das Kontrollkästchen tippst, füllt sich der Kreis im Uhrzeigersinn (200ms), dann erscheint ein Häkchen mit einem leichten Bounce (Spring-Animation), begleitet von einem sanften „Plink"-Geräusch und haptischem Feedback auf iOS. Die Zeile gleitet dann weg (150ms). Die gesamte Sequenz dauert etwa 500ms, aber dieses bewusste Timing erzeugt einen kleinen Dopamin-Kick, der das Abschließen von Aufgaben belohnend erscheinen lässt.
Ressourcen
- Website: culturedcode.com/things
- Design-Philosophie: Cultured Code Blogbeiträge über Einfachheit
- Tastaturkürzel: Integriertes Hilfe-Menü (Cmd+/)
- GTD-Integration: Wie Things die Getting Things Done-Methodik abbildet