Migrating to Raptor

Raptor is built by the author of Ignite’s HTML, Style, and Theme protocols—along with many other of its most widely used features. So the overall API will feel familiar, making migration quick and straightforward.

This guide highlights the most common changes. It’s not exhaustive, but it should get most sites about 80–90% of the way there.

Many Ignite types simply map to Raptor with updated naming.

Raptor Ignite
Post Article
PostPage ArticlePage
PostProcessor ArticleRenderer
InlineContent InlineElement
Page StaticPage
CategoryPage TagPage
Category Tag
Disclosure Accordion / Item
InlineText Span / Strong / Emphasis

In most cases, renames can be applied directly without changing behavior.


Site Configuration

Raptor consolidates theme configuration and uses a single themes array instead of separate light and dark theme properties.

Ignite

struct MySite: Site {
   var lightTheme: (any Theme)? = DefaultLightTheme()
   var darkTheme: (any Theme)? = nil

   var articlePages: [any ArticlePage] { ... }
   var staticPages: [any StaticPage] { ... }
}

Raptor

struct MySite: Site {
   var colorScheme: ColorScheme = .dark
   var themes: [any Theme] {
       MyTheme()
   }

   var postPages: [any PostPage] { ... }
   var pages: [any Page] { ... }
}

Themes

Raptor themes use a single type with color scheme branching, replacing Ignite’s separate light and dark theme implementations.

Ignite

// Base protocol with shared theme values
protocol BaseTheme: Theme {}

// Default implementation for shared values
extension BaseTheme {
   var accent: Color { Color(hex: "#FF0000") }
   var secondaryAccent: Color { Color(hex: "#00FF00") }
}

// Light theme implementation
struct LightTheme: BaseTheme {
   var colorScheme: ColorScheme = .light
   var syntaxHighlighterTheme: HighlighterTheme = .githubLight
}

// Dark theme implementation
struct DarkTheme: BaseTheme {
   var colorScheme: ColorScheme = .dark
   var syntaxHighlighterTheme: HighlighterTheme = .xcodeDark
}

Raptor

struct MyTheme: Theme {
   func theme(site: Content, colorScheme: ColorScheme) -> Content {
       if colorScheme == .light {
           site
               .syntaxHighlighterTheme(.githubLight)
       } else if colorScheme == .dark {
           site
               .syntaxHighlighterTheme(.xcodeDark)
       } else { // .any
           site
               .accent(Color(hex: "#FF0000"))
               .secondaryAccent(Color(hex: "#00FF00"))
       }
   }
}

Layout & Head Content

Raptor uses a single Main type instead of separate Head and Body elements. Scripts and other metadata are applied as modifiers rather than being nested inside Head.

Ignite

struct MainLayout: Layout {
   var body: some Document {
       Head {
           Script(file: "custom.js")
       }
       Body()
   }
}

Raptor

struct MainLayout: Layout {
   var body: some Document {
       Main()
           .script(file: "custom.js")
   }
}

Raptor uses explicit role assignment and layout controls like Spacer for flexible navigation structure.

Ignite

NavigationBar(logo: "My Site") {
   Link("Home", target: Home())
   Link("Docs", target: Docs())
}

Raptor

Navigation {
   InlineText("My Site")
       .navigationItemRole(.logo)

   Spacer()

   Link("Docs", target: Docs())
}

Length Units & Margins

Rather than the LengthUnit type, Raptor uses intent-specific layout modifiers like containerRelativeFrame(_:) and screenRelativeFrame(_:), making it clear what a view sizes itself relative to. This removes ambiguity and results in more predictable layouts.

Other than markdown text, all elements lack implicit margins and padding for more predictable spacing.


Grids

Ignite uses a fixed 12-column grid where layout is controlled with numeric widths. Raptor uses a row-driven API instead.

Ignite

Grid {
   Text("A")
      .width(4)
   Text("B")
      .width(4)
   Text("C")
      .width(4)
}

Raptor

Grid {
   GridRow {
       Text("A")
       Text("B")
       Text("C")
   }
}

Full-width content

In Ignite, spanning the full width requires setting width to 12:

Grid {
   Text("Name")
      .width(4)
   Text("Role")
      .width(4)
   Text("Location")
      .width(4)

   Text("Alex").width(12)
}

In Raptor, single items automatically span the grid width:

Grid {
   GridRow {
       Text("Name")
       Text("Role")
       Text("Location")
   }

   Text("Alex")
}

Modals & Popovers

Both frameworks support modals and popovers, but they differ in how presentations are triggered and managed.

Ignite

Button("Open settings") {
   ShowModal(id: "settings")
}

Modal(id: "settings") {
   Text("Settings")
}

Raptor

Button("Open settings", action: showModal("settings")
   .modal(id: "settings") {
      Text("Settings")
   }

Scroll Views

Ignite provides a dedicated Carousel type with slides, transitions, and background handling built in. Raptor uses a more general-purpose ScrollView that covers carousels and many custom use cases that require native scrolling, snapping, and automation.

Ignite

Carousel {
   Slide(background: "/images/photos/stack.jpg")
   Slide(background: "/images/photos/wind.jpg")
   Slide(background: "/images/photos/washing.jpg")
}
.carouselStyle(.crossfade(3))

Raptor

ScrollView(.horizontal, id: "gallery") {
   Image("/images/photos/stack.jpg")
   Image("/images/photos/wind.jpg")
   Image("/images/photos/washing.jpg")
}
.scrollBehavior(.viewAligned(.center))
.autoAdvance(every: 3)

Disclosures

Raptor uses individual Disclosure views instead of a container-based Accordion. Use matchedTransitionEffect(id:) to coordinate multiple disclosures so only one stays open at a time.

Ignite

Accordion {
   Item("Title") {
       Text("Content")
   }

   Item("Another title") {
       Text("More content")
   }
}

Raptor

Disclosure("Title") {
   Text("Content")
}
.matchedTransitionEffect(id: "myGroup")

Disclosure("Another title") {
   Text("More content")
}
.matchedTransitionEffect(id: "myGroup")

Happy migrating! 🚀