Skip to content

PoC: widgetAccentable prop + light-dark() color support (Track 1)#166

Draft
burczu wants to merge 2 commits into
callstackincubator:mainfrom
burczu:poc/widget-reactivity-track-1
Draft

PoC: widgetAccentable prop + light-dark() color support (Track 1)#166
burczu wants to merge 2 commits into
callstackincubator:mainfrom
burczu:poc/widget-reactivity-track-1

Conversation

@burczu
Copy link
Copy Markdown
Contributor

@burczu burczu commented May 29, 2026

Closes #165

Summary

Standalone rendering improvements for Voltra widgets on iOS. Adds two building blocks that enable
native SwiftUI environment adaptation without touching the server or the AppIntent pipeline:

  • widgetAccentable prop — a new boolean prop on all Voltra components, serialised as the
    short name wa. Applied via .widgetAccentable() in VoltraElementView so elements receive
    the system accent tint in accented / Liquid Glass rendering modes automatically.

  • light-dark() color support — CSS Color Level 4 light-dark(<light>, <dark>) syntax
    accepted anywhere a color string is used. Parsed in JSColorParser and stored as a
    (light: Color, dark: Color) pair; rendered via LightDarkForeground: ShapeStyle whose
    resolve(in: EnvironmentValues) is called by SwiftUI's rendering engine at draw time with the
    correct dark/light context.

Neither feature requires Swift from the developer. Both are purely additive — no existing widget
behaviour changes.

Developer experience

import { Voltra } from 'voltra'

export const MyWidget = () => (
  <Voltra.VStack style={{ flex: 1, padding: 16 }}>
    <Voltra.Text
      widgetAccentable
      style={{ fontSize: 22, color: 'light-dark(#111111, #eeeeee)' }}
    >
      Adaptive text
    </Voltra.Text>
  </Voltra.VStack>
)

What was built

Artifact Location Purpose
widgetAccentable prop packages/ios/src/jsx/baseProps.tsx, packages/core/src/payload/short-names.ts New wa short name; applies .widgetAccentable() SwiftUI modifier
JSColorParser.parseLightDarkComponents() packages/voltra/ios/ui/Style/JSColorParser.swift Parses light-dark(…) into (light: Color, dark: Color) pair
LightDarkForeground: ShapeStyle packages/voltra/ios/ui/Style/TextStyle.swift Adaptive ShapeStyle — resolve(in:) picks correct Color at draw time
StyleConverter update packages/voltra/ios/ui/Style/StyleConverter.swift Routes light-dark() strings to lightDarkColors field
VoltraText update packages/voltra/ios/ui/Views/VoltraText.swift Uses foregroundStyle(LightDarkForeground(…)) when lightDarkColors is set
XCTest suite packages/voltra/ios/Tests/JSColorParserTests.swift 118-line test file covering light-dark paths and existing smoke tests
Example widget example/widgets/ios/IosWeatherWidget.tsx Verification badges for manual testing

Exit criterion

  • widgetAccentable prop serialised as wa short name and .widgetAccentable() applied in renderer — implementation complete; visual effect requires real device verification (StandBy dimmed/night view triggers vibrant/accented rendering; simulator cannot replicate this)
  • light-dark() parsing implemented; adaptive LightDarkForeground: ShapeStyle wired into VoltraText — implementation complete; visual adaptation requires real device verification (iOS simulator does not re-render widgets on appearance toggle — confirmed with native Calendar widget as baseline)

Test plan

  • Build in Xcode — BUILD SUCCEEDED
  • Real device: StandBy dimmed/night view → widgetAccentable elements receive accent tint
  • Real device: toggle dark/light mode in Settings → light-dark() text color adapts

burczu added 2 commits May 29, 2026 21:47
…rt (iOS)

Adds two building blocks for widget UI reactivity without server round-trips:

- `widgetAccentable` boolean prop on all components; serialised as short name `wa`;
  applied via `.widgetAccentable()` SwiftUI modifier in VoltraElementView so elements
  receive the system accent tint in accented/Liquid Glass rendering mode
- `light-dark(<light>, <dark>)` CSS Color Level 4 syntax in JSColorParser; resolved
  to an adaptive UIColor that responds to UITraitCollection at render time
- Full XCTest suite for JSColorParser (light-dark paths + existing smoke tests)
- IosWeatherWidget updated with two PoC verification badges for manual testing
…und ShapeStyle

The UIColor(dynamicProvider:) approach does not produce adaptive colors in WidgetKit.
Replace with LightDarkForeground: ShapeStyle whose resolve(in: EnvironmentValues) is
called by SwiftUI's rendering engine at draw time with the correct dark/light context.

- JSColorParser: add parseLightDarkComponents() returning both Color values as a pair;
  parseLightDark() private fallback (UIColor) kept for non-text contexts (borders,
  backgrounds); remove findTopLevelComma() in favour of splitLightDark()
- TextStyle: add lightDarkColors field; add LightDarkForeground: ShapeStyle
- StyleConverter.parseText: route light-dark() strings to lightDarkColors instead of
  the flat color field
- VoltraText: switch from foregroundColor to foregroundStyle(LightDarkForeground(...))
  when lightDarkColors is set
@burczu burczu force-pushed the poc/widget-reactivity-track-1 branch from 0b4fc42 to 56b60ce Compare May 29, 2026 19:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Widget reactivity: support on-device state changes without a server push

1 participant