For teams who need deeper styling control while staying within the UIKit framework. Component Styling allows you to override CSS, customize individual components, and implement advanced theming.

Styling Control

Override styles with custom config json

Component Targeting

Customize specific components and pages

Advanced Theming

Complex color schemes and typography

UIKit Framework

Still within the UIKit ecosystem

When to Choose Component Styling

Component Styling is ideal when you need: Custom brand experiences - Basic colors and fonts
Granular control - Target specific components or pages
Advanced theming - Complex color schemes and typography
Framework benefits - Want to stay within UIKit’s ecosystem
Simple branding - Dynamic UI is easier for basic needs
Complex layouts - Fork & Extend offers more freedom
Custom components - Need source code access for new components

Configuration Model

Component Styling uses a single config.json surface for cross-platform overrides. It layers on top of default UIKit tokens (and Dynamic UI if present) using a path pattern: page_id/component_id/element_id with wildcards (*).

Path Pattern & Levels

LevelExampleDescriptionWildcard Use
Pagecamera_page/*/*Entire screen / routeReplace component & element with */*
Component*/comment_tray_component/*Functional unit within a pageWildcard page to apply globally
Elementstory_page/*/close_buttonSingle interactive / visual elementWildcard component for all instances
Specific beats generic: Element overrides > Component overrides > Page overrides > Global theme.

Supported Theme Tokens

TokenPurposeNotes
primary_colorActions / highlightsHigh contrast vs background
secondary_colorSecondary actions / bordersComplementary to primary
base_colorPrimary text colorBody & headings
base_shade1_colorSecondary textSubtitles / meta
base_shade2_colorTertiary text / hintsPlaceholders, inactive
base_shade3_colorDisabled textLow emphasis
base_shade4_colorOptional faint text / separatorsLowest emphasis
alert_colorErrors / destructiveDistinct from brand palette
background_colorSurface backgroundPage / component surface

Minimal Config Skeleton

{
  "preferred_theme": "default",
  "theme": {
    "light": { "primary_color": "#1054DE", "background_color": "#FFFFFF" },
    "dark": { "primary_color": "#1054DE", "background_color": "#191919" }
  },
  "excludes": [],
  "customizations": {}
}

Theme Application & Preferred Mode

Set the app’s theme resolution strategy with preferred_theme:
ValueBehavior
defaultFollow OS setting (light/dark)
lightForce light regardless of OS
darkForce dark regardless of OS
Use default unless you have a business requirement to lock theme (e.g., brand guidelines for launch).

Targeting Examples

Reference: Allowed IDs

select_target_page/*/*
camera_page/*/*
create_story_page/*/*
story_page/*/*

Exclusions

Remove (hide) specific elements by adding their path to excludes:
"excludes": [
  "create_story_page/*/aspect_ratio_button"
]
Excluding structural elements (like a required close button) may degrade usability. Test flows after any exclusion.

Precedence & Conflict Resolution

ScenarioWinning Value
Page vs GlobalPage override
Component vs PageComponent override
Element vs ComponentElement override
Multiple matches same levelLast in file (bottom-most)
Group related overrides together to prevent accidental shadowing by later blocks.

Best Practices

Token First

Change global tokens before per-element overrides.

Progressive Specificity

Escalate from page → component → element only when needed.

Consistency

Reuse colors; avoid random hex proliferation.

Auditable Changes

Document rationale in PR descriptions.

Dark Mode QA

Visually inspect every high-contrast surface.

Minimal Exclusions

Prefer styling adjustments over removal.

Overriding Navigation Behaviour

Navigation can be customized without forking by overriding the provided Behaviour classes. UIKit isolates navigation & event hooks into behaviour objects per page/component so you can inject custom flows (e.g., presenting a different creation screen, analytics, guards) while keeping styling overrides separate.

Behaviour Architecture

  • Each major screen/component exposes a Behaviour (iOS) / Behavior (Android) class with overridable navigation methods.
  • A top-level AmityUIKit4Manager.behaviour (iOS) / AmityUIKit4Manager.behavior (Android) aggregates these instances.
  • Replace only the concrete behaviour you need; leave others at default.

Override Example: Story Tab Navigation

// Custom behaviour for Story Tab component
class CustomStoryTabComponentBehaviour: AmityStoryTabComponentBehaviour {
  override init() { super.init() }

  override func goToViewStoryPage(context: AmityStoryTabComponentBehaviour.Context) {
    // Custom navigation (e.g., analytics, gated modal, custom host)
  }

  override func goToCreateStoryPage(context: AmityStoryTabComponentBehaviour.Context) {
    // Present alternative creation UI or permission flow
  }
}

// Register once (e.g. after SDK init)
func configureNavigationOverrides() {
  AmityUIKit4Manager.behaviour.storyTabComponentBehaviour = CustomStoryTabComponentBehaviour()
}
Only override the specific methods you need; keep defaults for forward compatibility.

Advanced iOS Navigation (Swipe Back & SwiftUI Hosting)

UIKit v4 pages are predominantly SwiftUI, embedded in UINavigationController via helper controllers:
ClassPurpose
AmitySwiftUIHostingControllerWrap a single SwiftUI view
AmitySwiftUIHostingNavigationControllerProvide a navigation stack hosting SwiftUI pages
To preserve the interactive swipe‑to‑go‑back gesture while customizing appearance, UIKit globally overrides navigation gesture delegate methods. You can supply your own gesture behaviour logic via AmitySwipeToBackGestureBehavior.
// Default usage
let defaultGestureBehavior = AmitySwipeToBackGestureBehavior()

// Custom gesture policy
class CustomSwipeBackGestureBehavior: AmitySwipeToBackGestureBehavior {
  override func gestureRecognizerShouldBegin(
    navigationController: UINavigationController,
    _ gestureRecognizer: UIGestureRecognizer
  ) -> Bool {
    // Example: disable swipe on root page or during modal overlay
    return navigationController.viewControllers.count > 1
  }

  override func gestureRecognizer(
    _ gestureRecognizer: UIGestureRecognizer,
    shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer
  ) -> Bool {
    // Allow simultaneous recognition with horizontally scrollable carousels
    return true
  }
}

func configureGestureBehavior() {
  AmityUIKit4Manager.behaviour.swipeToBackGestureBehavior = CustomSwipeBackGestureBehavior()
}
Keep gesture logic lightweight—long operations here can block UI responsiveness.

When to Escalate

If navigation changes require altering internal view composition (e.g., embedding a new container architecture), consider moving to Fork & Extend. Simple route substitutions, analytics, permission gates, and gesture tweaks belong here.

Next Steps