design/craft

4 min de leitura 887 palavras
design/craft screenshot

Craft: Excelência em Documentos Native-First

“Acreditamos que ferramentas para o pensamento devem parecer uma extensão dos seus pensamentos, não um obstáculo.”

O Craft prova que apps nativos podem entregar experiências que apps web não conseguem igualar. Construído com integração profunda de plataforma, ele alcança a responsividade e o polimento que fazem anotações digitais parecerem tão naturais quanto papel.


Principais Conclusões

  1. Nativo supera Electron - Tempo de resposta abaixo de 50ms requer UI específica por plataforma, não wrappers web
  2. Páginas dentro de páginas - Qualquer bloco pode se tornar uma página, permitindo que a estrutura emerja do conteúdo
  3. Múltiplos layouts, mesmos dados - Listas, cards, galerias e quadros dão aos usuários a visualização certa para cada contexto
  4. Publicação web com um clique - Páginas compartilhadas funcionam sem contas de destinatários ou downloads de apps
  5. Apropriado à plataforma, não idêntico - Craft no Mac parece Mac; Craft no iOS parece iOS

Por Que o Craft Importa

O Craft ganhou o Apple Design Award 2021 e consistentemente recebe reconhecimento da App Store por se recusar a comprometer performance nativa por conveniência cross-platform.

Conquistas principais: - Apps verdadeiramente nativos no iOS, macOS, Windows (não Electron) - Tempo de resposta abaixo de 50ms em todas as interações - Offline-first com sincronização perfeita - Arquitetura baseada em blocos sem a lentidão de apps web - Páginas de compartilhamento bonitas que funcionam sem contas


Filosofia Central de Design

Native-First, Não Web-Wrapped

A escolha definidora do Craft: construir apps nativos para cada plataforma, usando lógica de negócios compartilhada mas UI específica por plataforma.

ABORDAGEM ELECTRON/WEB             ABORDAGEM NATIVA DO CRAFT
───────────────────────────────────────────────────────────────────
Código único (JavaScript)          UI específica por plataforma (Swift, etc.)
Motor de renderização web          Renderização nativa
"Consistência" cross-platform      Comportamento apropriado à plataforma
~200ms de latência de input        ~16ms de latência de input
Atalhos de teclado genéricos       Atalhos nativos da plataforma
Seleção de texto padrão web        Motor de texto nativo

Insight chave: Usuários não querem apps que pareçam iguais em todo lugar—eles querem apps que pareçam certos na sua plataforma.

macOS via Catalyst (Feito Corretamente)

O Craft usa Mac Catalyst mas o customiza pesadamente para parecer verdadeiramente nativo do Mac:

// Abordagem de customização Catalyst do Craft
#if targetEnvironment(macCatalyst)
extension SceneDelegate {
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession) {
        guard let windowScene = scene as? UIWindowScene else { return }

        // Habilitar toolbar nativa do Mac
        if let titlebar = windowScene.titlebar {
            titlebar.titleVisibility = .hidden
            titlebar.toolbar = createNativeToolbar()
        }

        // Dimensionamento de janela específico do Mac
        windowScene.sizeRestrictions?.minimumSize = CGSize(width: 800, height: 600)

        // Habilitar abas de janela
        UIApplication.shared.connectedScenes
            .compactMap { $0 as? UIWindowScene }
            .forEach { $0.titlebar?.toolbar?.showsBaselineSeparator = false }
    }

    private func createNativeToolbar() -> NSToolbar {
        let toolbar = NSToolbar(identifier: "CraftToolbar")
        toolbar.displayMode = .iconOnly
        toolbar.delegate = self
        return toolbar
    }
}
#endif

Biblioteca de Padrões

1. Arquitetura de Conteúdo Baseada em Blocos

Todo elemento no Craft é um bloco. Todo bloco com conteúdo é potencialmente uma página, criando aninhamento infinito sem sobrecarga cognitiva.

Tipos de blocos:

BLOCOS DE TEXTO
├── Parágrafo (padrão)
├── Título 1, 2, 3
├── Citação
├── Destaque (info, aviso, sucesso)
└── Código (com realce de sintaxe)

BLOCOS DE MÍDIA
├── Imagem (com legenda)
├── Vídeo
├── Anexo de arquivo
├── Desenho (Apple Pencil)
└── Gravação de áudio

BLOCOS ESTRUTURAIS
├── Toggle (recolhível)
├── Página (documento aninhado)
├── Divisor
└── Tabela

Conceito de implementação:

protocol CraftBlock: Identifiable {
    var id: UUID { get }
    var content: BlockContent { get set }
    var style: BlockStyle { get set }
    var children: [any CraftBlock] { get set }
    var metadata: BlockMetadata { get }
}

enum BlockContent {
    case text(AttributedString)
    case image(ImageData)
    case page(PageReference)
    case toggle(isExpanded: Bool)
    case code(language: String, content: String)
    case table(TableData)
}

struct BlockMetadata {
    let created: Date
    let modified: Date
    let createdBy: UserReference?
}

// Todo bloco pode conter outros blocos
class PageBlock: CraftBlock {
    var id = UUID()
    var content: BlockContent
    var style: BlockStyle
    var children: [any CraftBlock] = []
    var metadata: BlockMetadata

    // Uma página é apenas um bloco que pode ser aberto em tela cheia
    var canOpenAsPage: Bool { true }
}

Insight chave: Quando todo bloco pode ser uma página, a arquitetura da informação emerge do conteúdo, não de uma estrutura predeterminada.


2. Páginas Aninhadas (Páginas Dentro de Páginas)

A funcionalidade característica do Craft: qualquer bloco pode se tornar uma página, e páginas se aninham infinitamente.

ESTRUTURA DO DOCUMENTO
───────────────────────────────────────────────────────────────────
📄 Projeto Alpha
├── 📝 Parágrafo de introdução
├── 📄 Notas de Pesquisa        ← Esta é uma página (documento aninhado)
│   ├── 📝 Entrevistas com usuários
│   ├── 📄 Entrevista: Sarah    ← Outra página aninhada
│   │   └── 📝 Insights principais
│   └── 📝 Análise competitiva
├── 📝 Visão geral do cronograma
└── 📄 Notas de Reunião         ← Outra página
    └── 📝 Itens de ação

Navegação: Trilha de breadcrumb mostra o caminho
Projeto Alpha > Notas de Pesquisa > Entrevista: Sarah

Padrão de implementação SwiftUI:

struct PageView: View {
    @Bindable var page: PageDocument
    @State private var selectedBlock: CraftBlock?

    var body: some View {
        ScrollView {
            LazyVStack(alignment: .leading, spacing: 0) {
                ForEach(page.blocks) { block in
                    BlockView(block: block, onTap: { tapped in
                        if tapped.canOpenAsPage {
                            navigateToPage(tapped)
                        } else {
                            selectedBlock = tapped
                        }
                    })
                }
            }
        }
        .navigationTitle(page.title)
        .toolbar {
            // Navegação breadcrumb
            ToolbarItem(placement: .principal) {
                BreadcrumbView(path: page.ancestorPath)
            }
        }
    }
}

struct BreadcrumbView: View {
    let path: [PageReference]

    var body: some View {
        HStack(spacing: 4) {
            ForEach(Array(path.enumerated()), id: \.element.id) { index, page in
                if index > 0 {
                    Image(systemName: "chevron.right")
                        .font(.caption)
                        .foregroundStyle(.secondary)
                }
                Button(page.title) {
                    navigateTo(page)
                }
                .buttonStyle(.plain)
                .foregroundStyle(index == path.count - 1 ? .primary : .secondary)
            }
        }
    }
}

3. Sistema de Layout de Cards

O Craft oferece 5 estilos visuais para páginas, tornando documentos escaneáveis num relance.

Estilos de cards:

ESTILO 1: LISTA (Padrão)
┌────────────────────────────────────────┐
│ 📄 Título da Página                    │
│    Texto de preview aparece aqui...    │
└────────────────────────────────────────┘

ESTILO 2: CARD (Médio)
┌──────────────────┐
│ ┌──────────────┐ │
│ │   [Imagem]   │ │
│ └──────────────┘ │
│ Título da Página │
│ Texto preview... │
└──────────────────┘

ESTILO 3: CARD (Grande)
┌────────────────────────────────────────┐
│ ┌────────────────────────────────────┐ │
│ │                                    │ │
│ │         [Imagem de Capa]           │ │
│ │                                    │ │
│ └────────────────────────────────────┘ │
│ Título da Página                       │
│ Texto preview mais longo com espaço... │
└────────────────────────────────────────┘

ESTILO 4: GALERIA (Grid)
┌────────┐ ┌────────┐ ┌────────┐
│ [Img]  │ │ [Img]  │ │ [Img]  │
│ Título │ │ Título │ │ Título │
└────────┘ └────────┘ └────────┘

ESTILO 5: QUADRO (Estilo Kanban)
│ A Fazer  │ Fazendo  │ Feito    │
├──────────┼──────────┼──────────┤
│ Tarefa 1 │ Tarefa 3 │ Tarefa 5 │
│ Tarefa 2 │ Tarefa 4 │          │

Implementação:

enum PageLayoutStyle: String, CaseIterable {
    case list = "list"
    case cardMedium = "card_medium"
    case cardLarge = "card_large"
    case gallery = "gallery"
    case board = "board"
}

struct PageGridView: View {
    let pages: [PageReference]
    let style: PageLayoutStyle

    var body: some View {
        switch style {
        case .list:
            LazyVStack(spacing: 8) {
                ForEach(pages) { page in
                    ListRowView(page: page)
                }
            }

        case .cardMedium, .cardLarge:
            let columns = style == .cardMedium ? 3 : 2
            LazyVGrid(columns: Array(repeating: .init(.flexible()), count: columns)) {
                ForEach(pages) { page in
                    CardView(page: page, size: style == .cardLarge ? .large : .medium)
                }
            }

        case .gallery:
            LazyVGrid(columns: Array(repeating: .init(.flexible()), count: 4)) {
                ForEach(pages) { page in
                    GalleryThumbnail(page: page)
                }
            }

        case .board:
            BoardView(pages: pages)
        }
    }
}

4. Fundos de Página e Temas

Cada página pode ter sua própria identidade visual através de fundos e cores de destaque.

struct PageAppearance {
    // Opções de fundo
    enum Background {
        case solid(Color)
        case gradient(Gradient)
        case image(ImageReference)
        case paper(PaperTexture)
    }

    // Texturas de papel para sensação de escrita
    enum PaperTexture: String {
        case none
        case lined
        case grid
        case dotted
    }

    var background: Background = .solid(.white)
    var accentColor: Color = .blue
    var iconEmoji: String?
    var coverImage: ImageReference?
}

// Aplicado à página
struct PageContainer: View {
    let page: PageDocument

    var body: some View {
        ZStack {
            // Camada de fundo
            backgroundView(for: page.appearance.background)

            // Camada de conteúdo
            PageContentView(page: page)
        }
    }

    @ViewBuilder
    private func backgroundView(for background: PageAppearance.Background) -> some View {
        switch background {
        case .solid(let color):
            color.ignoresSafeArea()
        case .gradient(let gradient):
            LinearGradient(gradient: gradient, startPoint: .top, endPoint: .bottom)
                .ignoresSafeArea()
        case .image(let ref):
            AsyncImage(url: ref.url) { image in
                image.resizable().scaledToFill()
            } placeholder: {
                Color.gray.opacity(0.1)
            }
            .ignoresSafeArea()
            .overlay(.ultraThinMaterial)
        case .paper(let texture):
            PaperTextureView(texture: texture)
        }
    }
}

5. Páginas de Compartilhamento (Publicação Web)

O Craft gera páginas web bonitas e responsivas a partir de documentos. Não é necessária conta para visualizar.

DOCUMENTO NO CRAFT                PÁGINA DE COMPARTILHAMENTO NA WEB
───────────────────────────────────────────────────────────────────
📄 Proposta de Projeto            https://www.craft.do/s/abc123
├── 📝 Resumo Executivo     →
├── 📄 Detalhes do Orçamento      Layout limpo e responsivo
├── 📝 Cronograma                 Tipografia preservada
└── 📄 Bios da Equipe             Imagens otimizadas
                                  Modo escuro suportado
                                  Não precisa de conta Craft

Recursos principais: - Publicação com um clique - Domínios personalizados disponíveis - Opção de proteção por senha - Analytics de visualizações - Renderização amigável para SEO - Responsivo em todos os dispositivos


Sistema de Design Visual

Paleta de Cores

extension Color {
    // Paleta característica do Craft
    static let craftPurple = Color(hex: "#6366F1")  // Destaque primário
    static let craftBackground = Color(hex: "#FAFAFA")  // Modo claro
    static let craftSurface = Color(hex: "#FFFFFF")

    // Cores semânticas
    static let craftSuccess = Color(hex: "#10B981")
    static let craftWarning = Color(hex: "#F59E0B")
    static let craftError = Color(hex: "#EF4444")
    static let craftInfo = Color(hex: "#3B82F6")

    // Hierarquia de texto
    static let craftTextPrimary = Color(hex: "#111827")
    static let craftTextSecondary = Color(hex: "#6B7280")
    static let craftTextMuted = Color(hex: "#9CA3AF")
}

Tipografia

struct CraftTypography {
    // Tipografia do documento
    static let title = Font.system(size: 32, weight: .bold, design: .default)
    static let heading1 = Font.system(size: 28, weight: .semibold)
    static let heading2 = Font.system(size: 22, weight: .semibold)
    static let heading3 = Font.system(size: 18, weight: .medium)
    static let body = Font.system(size: 16, weight: .regular)
    static let caption = Font.system(size: 14, weight: .regular)

    // Blocos de código
    static let code = Font.system(size: 14, weight: .regular, design: .monospaced)

    // Alturas de linha (generosas para legibilidade)
    static let bodyLineSpacing: CGFloat = 8
    static let headingLineSpacing: CGFloat = 4
}

Sistema de Espaçamento

struct CraftSpacing {
    // Espaçamento de blocos
    static let blockGap: CGFloat = 4        // Entre blocos
    static let sectionGap: CGFloat = 24     // Entre seções
    static let pageMargin: CGFloat = 40     // Bordas da página (desktop)
    static let mobileMargin: CGFloat = 16   // Bordas da página (mobile)

    // Largura do conteúdo
    static let maxContentWidth: CGFloat = 720  // Leitura ideal
    static let wideContentWidth: CGFloat = 960 // Tabelas, galerias
}

Filosofia de Animação

Transições de Página Suaves

// Navegação entre páginas usa matched geometry
struct NavigationTransition: View {
    @Namespace private var namespace
    @State private var selectedPage: PageReference?

    var body: some View {
        ZStack {
            // Lista de páginas
            PageListView(
                onSelect: { page in
                    withAnimation(.spring(response: 0.35, dampingFraction: 0.85)) {
                        selectedPage = page
                    }
                },
                namespace: namespace
            )

            // Página selecionada expande da posição do card
            if let page = selectedPage {
                PageDetailView(page: page)
                    .matchedGeometryEffect(id: page.id, in: namespace)
                    .transition(.scale.combined(with: .opacity))
            }
        }
    }
}

Animações de Blocos

// Blocos animam ao reordenar
struct AnimatedBlockList: View {
    @Binding var blocks: [CraftBlock]

    var body: some View {
        LazyVStack(spacing: CraftSpacing.blockGap) {
            ForEach(blocks) { block in
                BlockView(block: block)
                    .transition(.asymmetric(
                        insertion: .move(edge: .top).combined(with: .opacity),
                        removal: .move(edge: .bottom).combined(with: .opacity)
                    ))
            }
        }
        .animation(.spring(response: 0.3, dampingFraction: 0.8), value: blocks)
    }
}

Lições para Nosso Trabalho

1. Performance Nativa Vale o Investimento

Usuários sentem a diferença entre latência de 16ms e 200ms. Apps nativos vencem na sensação.

2. Páginas Dentro de Páginas Permitem Organização Orgânica

Deixe a estrutura emergir do conteúdo. Não force usuários a decidir hierarquia antecipadamente.

3. Múltiplos Layouts Visuais para o Mesmo Conteúdo

Cards, listas, galerias—mesmos dados, visualizações diferentes para contextos diferentes.

4. Compartilhar Sem Atrito

Publicação web com um clique remove completamente o problema "como compartilho isso?".

5. Apropriado à Plataforma, Não Idêntico à Plataforma

Craft no Mac parece um app de Mac. Craft no iOS parece um app de iOS. Esse é o objetivo.


Perguntas Frequentes

Como o Craft é diferente do Notion?

O Craft é nativo (construído especificamente para plataformas Apple), enquanto o Notion é baseado em web. Isso significa que o Craft alcança tempos de resposta abaixo de 50ms, funciona totalmente offline e se integra profundamente com recursos do iOS/macOS como Apple Pencil, Atalhos e busca do sistema. O Notion oferece mais recursos de banco de dados; o Craft prioriza a experiência de escrita.

Posso usar o Craft offline?

Sim. O Craft armazena todos os documentos localmente e sincroniza via iCloud quando conectado. Você pode criar, editar e organizar documentos sem internet. As alterações sincronizam automaticamente quando você reconecta.

O que acontece quando compartilho uma página do Craft?

O Craft gera uma página web responsiva em uma URL craft.do. Os destinatários visualizam em qualquer navegador sem criar uma conta ou instalar o app. A página preserva a tipografia, imagens e estrutura de páginas aninhadas do seu documento.

O Craft suporta Markdown?

O Craft usa seu próprio formato de blocos mas exporta para Markdown. Você pode copiar conteúdo como Markdown ou exportar documentos inteiros. Alguns atalhos de Markdown funcionam durante a edição (como # para títulos), mas o Craft enfatiza edição visual sobre marcação de texto puro.

Como funcionam as páginas aninhadas no Craft?

Qualquer bloco de texto pode se tornar uma página pressionando o ícone de página ou digitando /page. Páginas aninhadas aparecem inline no documento pai e podem ser abertas em tela cheia. A navegação breadcrumb mostra sua localização na hierarquia. Isso cria organização natural sem forçar estruturas de pastas antecipadamente.