Things 3: A Arte da Simplicidade Focada
Como o Things 3 alcançou simplicidade através de restrições: hierarquia natural, separação entre Quando vs Prazo, design orientado ao teclado e uso restrito e significativo de cores. Com padrões de implementação em SwiftUI.
Things 3: A Arte da Simplicidade Focada
“Acreditamos que o simples é difícil.” — Cultured Code
Things 3 é frequentemente chamado de o gerenciador de tarefas mais bonito já criado. Essa beleza emerge do foco implacável no que importa e da eliminação de todo o resto.
Por Que o Things 3 Importa
Things 3 demonstra que restrições criam clareza. Enquanto concorrentes acumulam recursos (tarefas recorrentes com 47 opções, projetos com dependências e gráficos de Gantt), Things pergunta: o que uma pessoa realmente precisa para fazer as coisas?
Principais conquistas: - Alcançou simplicidade visual sem sacrificar profundidade - Tornou datas significativas (Hoje, Noite, Algum Dia) - Criou uma hierarquia que espelha como humanos pensam sobre trabalho - Provou que priorizar teclado pode ser bonito - Estabeleceu o padrão para design de apps nativos macOS/iOS
Principais Lições
- Separe “quando vou fazer isso?” de “quando vence?” - O modelo de datas duplas do Things (Quando + Prazo) elimina a ambiguidade que assola a maioria dos apps de tarefas; intenção de agenda e prazos rígidos servem propósitos diferentes
- Algum Dia é um recurso, não um estado de falha - Dar às ideias um lar sem pressão previne ansiedade de caixa de entrada; tarefas em Algum Dia não poluem o Hoje nem provocam culpa
- Áreas nunca completam, Projetos sim - Essa distinção espelha a cognição humana: “Trabalho” é um domínio de vida contínuo, “Lançar v2.0” tem uma linha de chegada; misturar isso cria confusão
- Reserve cor para significado - A interface do Things é 95% neutra (preto, branco, cinza); cor aparece apenas para informação semântica (amarelo=Hoje, vermelho=Prazo, índigo=Noite)
- Conclusão deve ser recompensadora - A animação do checkbox (preencher → marca de verificação → som) leva 500ms mas entrega dopamina; completar tarefas se torna intrinsecamente motivador
Princípios Fundamentais de Design
1. A Hierarquia Natural
Things organiza o trabalho da forma como humanos pensam sobre ele: do amplo (áreas da vida) ao específico (tarefas individuais).
HIERARQUIA DO THINGS:
Área (Trabalho, Pessoal, Saúde) ← Categorias amplas de vida
│
└── Projeto (Lançar App) ← Meta finita com tarefas
│
└── Título (Design) ← Agrupamento opcional
│
└── Tarefa ← Item único acionável
│
└── Checklist ← Sub-etapas dentro da tarefa
Exemplo:
┌─────────────────────────────────────────────────────────────┐
│ [A] TRABALHO (Área) │
│ ├── [P] Lançar Versão 2.0 (Projeto) │
│ │ ├── [H] Design │
│ │ │ ├── [ ] Criar novos ícones │
│ │ │ └── [ ] Atualizar paleta de cores │
│ │ └── [H] Desenvolvimento │
│ │ ├── [ ] Implementar fluxo de auth │
│ │ └── [ ] Adicionar analytics │
│ └── [P] Redesign do Site (Projeto) │
│ └── [ ] Escrever copy para landing page │
└─────────────────────────────────────────────────────────────┘
O Insight Principal: Áreas nunca completam (são categorias de vida). Projetos completam (entregar até sexta). A distinção elimina paralisia de planejamento.
Modelo de Dados:
// Hierarquia do Things expressa em código
struct Area: Identifiable {
let id: UUID
var title: String
var projects: [Project]
var tasks: [Task] // Tarefas soltas não em projetos
}
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. Quando, Não Apenas Vencimento
Things separa “quando vou fazer isso?” (Hoje, Noite, Algum Dia) de “quando isso precisa estar pronto?” (Prazo). A maioria dos apps confunde essas coisas.
MODELO DE DATA TRADICIONAL:
┌──────────────────────────────────────────────────────────────┐
│ Tarefa: Escrever relatório │
│ Vencimento: 15 de março │
│ │
│ Problema: 15 de março é quando vou fazer ou quando vence? │
│ Se é vencimento, quando devo começar? │
└──────────────────────────────────────────────────────────────┘
MODELO DE DATA DO THINGS:
┌──────────────────────────────────────────────────────────────┐
│ Tarefa: Escrever relatório │
│ Quando: Hoje (vou trabalhar nisso hoje) │
│ Prazo: 15 de março (precisa estar completo até lá) │
│ │
│ Clareza: Duas perguntas separadas, duas respostas separadas │
└──────────────────────────────────────────────────────────────┘
LISTAS INTELIGENTES (filtragem automática):
┌───────────────────┬─────────────────────────────────────────┐
│ [I] CAIXA ENTRADA │ Capturas não categorizadas │
├───────────────────┼─────────────────────────────────────────┤
│ [*] HOJE │ when == .today OR deadline == today │
├───────────────────┼─────────────────────────────────────────┤
│ [E] ESTA NOITE │ when == .evening │
├───────────────────┼─────────────────────────────────────────┤
│ [D] EM BREVE │ when == .specificDate (futuro) │
├───────────────────┼─────────────────────────────────────────┤
│ [A] A QUALQUER HR │ when == nil AND deadline == nil │
├───────────────────┼─────────────────────────────────────────┤
│ [S] ALGUM DIA │ when == .someday │
├───────────────────┼─────────────────────────────────────────┤
│ [L] REGISTRO │ isComplete == true │
└───────────────────┴─────────────────────────────────────────┘
O Insight Principal: “Algum Dia” não é um estado de falha—é uma válvula de escape de pressão. Ideias têm um lugar sem poluir o Hoje.
Implementação SwiftUI:
struct WhenPicker: View {
@Binding var schedule: TaskSchedule?
@Binding var deadline: Date?
@State private var showDatePicker = false
var body: some View {
VStack(spacing: 12) {
// Opções rápidas
HStack(spacing: 8) {
WhenButton(
icon: "star.fill",
label: "Hoje",
color: .yellow,
isSelected: schedule == .today
) {
schedule = .today
}
WhenButton(
icon: "moon.fill",
label: "Noite",
color: .indigo,
isSelected: schedule == .evening
) {
schedule = .evening
}
WhenButton(
icon: "calendar",
label: "Escolher Data",
color: .red,
isSelected: showDatePicker
) {
showDatePicker = true
}
WhenButton(
icon: "archivebox.fill",
label: "Algum Dia",
color: .brown,
isSelected: schedule == .someday
) {
schedule = .someday
}
}
// Prazo (separado de "quando")
if let deadline = deadline {
HStack {
Image(systemName: "flag.fill")
.foregroundStyle(.red)
Text("Prazo: \(deadline, style: .date)")
Spacer()
Button("Limpar") { 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. Teclado Primeiro, Amigável ao Mouse
Things pode ser operado inteiramente via teclado com eficiência de memória muscular, mas permanece intuitivo para usuários de mouse.
ATALHOS DE TECLADO (padrões memorizáveis):
NAVEGAÇÃO:
Cmd+1-6 Ir para listas inteligentes (Caixa de Entrada, Hoje, etc.)
Cmd+Up/Down Mover entre seções
Space Quick Look (visualizar detalhes da tarefa)
MANIPULAÇÃO DE TAREFAS:
Cmd+K Completar tarefa
Cmd+S Agendar (seletor de Quando)
Cmd+Shift+D Definir prazo
Cmd+Shift+M Mover para projeto
Cmd+Shift+T Adicionar tags
CRIAÇÃO:
Space/Return Criar nova tarefa (sensível ao contexto)
Cmd+N Nova tarefa na Caixa de Entrada
Cmd+Opt+N Novo projeto
Entrada Rápida (tecla de atalho global):
Ctrl+Space Abre entrada rápida flutuante de qualquer lugar
┌─────────────────────────────────────────────────┐
│ [ ] | │
│ Nova tarefa aparece onde você estiver │
└─────────────────────────────────────────────────┘
O Insight Principal: Atalhos devem ser descobríveis mas não obrigatórios. A interface sugere teclas sem exigi-las.
Padrão CSS (Dicas de Atalho):
.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;
}
/* Mostrar ao passar o mouse */
.action-button:hover .shortcut-hint {
opacity: 1;
}
/* Sempre visível quando focado via teclado */
.action-button:focus-visible .shortcut-hint {
opacity: 1;
}
Entrada Rápida SwiftUI:
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 (apenas visual)
Circle()
.strokeBorder(.tertiary, lineWidth: 1.5)
.frame(width: 20, height: 20)
// Entrada de tarefa
TextField("Nova Tarefa", 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 }
// Criar tarefa na Caixa de Entrada
TaskStore.shared.createTask(title: taskTitle)
taskTitle = ""
}
}
4. Contenção Visual
Things usa cor com moderação, reservando-a apenas para significado. A maior parte da interface é neutra, permitindo que elementos coloridos se destaquem.
USO DE COR NO THINGS:
Neutro (95% da interface):
├── Fundo: Branco puro / Cinza escuro
├── Texto: Preto / Branco
├── Bordas: Cinza muito sutil
└── Ícones: Monocromático
Cor com Significado (5%):
├── Amarelo [*] = Hoje
├── Vermelho [!] = Prazo / Atrasado
├── Azul [#] = Tags (definidas pelo usuário)
├── Verde [x] = Animação de conclusão
└── Índigo [E] = Noite
EXEMPLO VISUAL:
┌─────────────────────────────────────────────────────────────┐
│ HOJE [*] (amarelo) │
├─────────────────────────────────────────────────────────────┤
│ ( ) Escrever documentação │
│ Nome do Projeto - [!] Vence amanhã (vermelho) │
│ │
│ ( ) Revisar pull requests │
│ [#] trabalho (tag azul) │
│ │
│ ( ) Ligar para dentista │
│ [E] Esta Noite (badge índigo) │
└─────────────────────────────────────────────────────────────┘
Observe: A maior parte do texto é preta. Cor só aparece onde carrega significado.
O Insight Principal: Quando tudo é colorido, nada se destaca. Reserve cor para informação.
Padrão CSS:
:root {
/* Paleta neutra (maior parte da UI) */
--surface-primary: #ffffff;
--surface-secondary: #f5f5f7;
--text-primary: #1d1d1f;
--text-secondary: #86868b;
--border: rgba(0, 0, 0, 0.08);
/* Cores semânticas (usadas com moderação) */
--color-today: #f5c518;
--color-deadline: #ff3b30;
--color-evening: #5856d6;
--color-complete: #34c759;
--color-tag: #007aff;
}
/* Linha de tarefa - majoritariamente neutra */
.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);
}
/* Cor apenas para significado */
.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. Conclusão Prazerosa
A animação de conclusão entrega recompensa, não mero feedback. Um som satisfatório de “plink” e uma animação suave de marca de verificação fazem completar tarefas parecer bom.
SEQUÊNCIA DE CONCLUSÃO:
Frame 1: ○ Título da tarefa
Frame 2-4: ◔ (círculo preenche no sentido horário)
Frame 5-7: ● (totalmente preenchido, breve pausa)
Frame 8-10: ✓ (marca aparece, escala ligeiramente para cima)
Frame 11+: Linha desliza para fora, lista fecha o espaço
Áudio: Som suave de "plink" no Frame 7-8 (momento de conclusão)
Háptico: Toque leve no iOS na conclusão
Implementação SwiftUI:
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 {
// Círculo de fundo
Circle()
.strokeBorder(.tertiary, lineWidth: 1.5)
.frame(width: 22, height: 22)
// Animação de preenchimento
Circle()
.trim(from: 0, to: fillAmount)
.stroke(.green, lineWidth: 1.5)
.frame(width: 22, height: 22)
.rotationEffect(.degrees(-90))
// Marca de verificação
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() {
// Fase 1: Preencher o círculo
withAnimation(.easeInOut(duration: 0.2)) {
animationPhase = .filling
}
// Fase 2: Mostrar marca de verificação com bounce
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
withAnimation(.spring(response: 0.3, dampingFraction: 0.5)) {
animationPhase = .checked
}
}
// Fase 3: Estabilizar e marcar como completo
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
withAnimation(.easeOut(duration: 0.15)) {
animationPhase = .done
}
isComplete = true
}
}
}
Padrões Transferíveis
Padrão 1: Detalhes Progressivos de Tarefa
Detalhes da tarefa expandem inline em vez de navegar para uma view separada.
struct ExpandableTask: View {
let task: Task
@State private var isExpanded = false
var body: some View {
VStack(alignment: .leading, spacing: 0) {
// Linha sempre visível
TaskRow(task: task, isExpanded: $isExpanded)
// Detalhes expansíveis
if isExpanded {
TaskDetails(task: task)
.padding(.leading, 44) // Alinhar com título
.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)
}
}
Padrão 2: Entrada em Linguagem Natural
Things interpreta linguagem natural para definir datas: “amanhã”, “semana que vem”, “15 de junho”.
function parseNaturalDate(input: string): TaskSchedule | null {
const lower = input.toLowerCase();
// Atalhos para Hoje
if (['hoje', 'hj', 'agora'].includes(lower)) {
return { type: 'today' };
}
// Noite
if (['hoje à noite', 'noite', 'de noite'].includes(lower)) {
return { type: 'evening' };
}
// Amanhã
if (['amanhã', 'amanha', 'amh'].includes(lower)) {
const date = new Date();
date.setDate(date.getDate() + 1);
return { type: 'date', date };
}
// Algum Dia
if (['algum dia', 'depois', 'eventualmente'].includes(lower)) {
return { type: 'someday' };
}
// Dias relativos
const inMatch = lower.match(/em (\d+) dias?/);
if (inMatch) {
const date = new Date();
date.setDate(date.getDate() + parseInt(inMatch[1]));
return { type: 'date', date };
}
// Nomes de dias
const days = ['domingo', 'segunda', 'terça', 'quarta', 'quinta', 'sexta', 'sábado'];
const dayIndex = days.findIndex(d => lower.startsWith(d.slice(0, 3)));
if (dayIndex !== -1) {
const date = getNextDayOfWeek(dayIndex);
return { type: 'date', date };
}
return null;
}
Padrão 3: Arrastar e Soltar Baseado em Lista
Reordenação é manipulação direta com feedback visual claro.
.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;
}
Lições de Design
- Separe preocupações: “Quando vou fazer isso?” vs “Quando vence?” são perguntas diferentes
- Contenção com cor: Reserve cor para significado; neutro é o padrão
- Conclusão é recompensa: Faça terminar tarefas parecer bom
- Teclado acelera, mouse acolhe: Projete para ambos sem comprometer nenhum
- Hierarquias naturais: Espelhe como humanos já pensam sobre trabalho (áreas → projetos → tarefas)
- Algum Dia é um recurso: Dê às ideias um lar que não crie pressão
Perguntas Frequentes
Qual é a diferença entre “Quando” e “Prazo” no Things 3?
“Quando” responde “quando vou trabalhar nisso?” (Hoje, Esta Noite, Algum Dia, ou uma data específica). “Prazo” responde “quando isso precisa estar completo?” A maioria dos apps de tarefas confunde esses conceitos, mas eles servem propósitos diferentes. Uma tarefa pode vencer na sexta (Prazo), mas você planeja trabalhar nela na quarta (Quando). Essa separação permite planejar seu dia em torno de intenções enquanto ainda rastreia prazos rígidos.
Como funciona a hierarquia do Things 3 (Áreas, Projetos, Tarefas)?
Áreas são categorias amplas de vida que nunca completam (Trabalho, Pessoal, Saúde). Projetos são metas finitas com um estado final claro (Lançar App, Planejar Férias). Tarefas são itens únicos acionáveis. Títulos opcionalmente agrupam tarefas dentro de projetos. Isso espelha como humanos naturalmente pensam sobre trabalho: responsabilidades contínuas contêm metas com prazo determinado, que contêm ações discretas.
Por que o Things 3 usa tão pouca cor?
Things reserva cor exclusivamente para significado semântico. A interface é 95% neutra (preto, branco, cinza) para que quando cor apareça, ela comunique: amarelo significa Hoje, vermelho significa Prazo/Atrasado, índigo significa Noite, azul marca tags. Se tudo fosse colorido, nada se destacaria. A contenção torna as cores significativas impossíveis de ignorar.
O que é “Algum Dia” no Things 3 e por que é valioso?
Algum Dia é um espaço dedicado para tarefas que você quer lembrar mas não quer poluindo suas listas ativas. Não é um estado de falha—é uma válvula de escape de pressão. Ideias para “algum dia” (aprender espanhol, ler aquele livro) têm um lar sem criar culpa diária. Você pode revisar Algum Dia semanalmente e promover tarefas para Hoje quando estiver pronto.
Como funciona a animação de conclusão do Things 3?
Quando você toca no checkbox, o círculo preenche no sentido horário (200ms), então uma marca de verificação aparece com um leve bounce (animação de mola), acompanhada de um som suave de “plink” e feedback háptico no iOS. A linha então desliza para fora (150ms). A sequência total leva cerca de 500ms, mas esse timing deliberado cria uma pequena dose de dopamina que faz completar tarefas parecer recompensador.
Recursos
- Website: culturedcode.com/things
- Filosofia de design: Posts do blog da Cultured Code sobre simplicidade
- Atalhos de teclado: Menu de Ajuda integrado (Cmd+/)
- Integração GTD: Como Things mapeia para a metodologia Getting Things Done