diff --git a/Sources/WebUI/Styles/Effects/ViewTransition/DocumentViewTransition.swift b/Sources/WebUI/Styles/Effects/ViewTransition/DocumentViewTransition.swift
new file mode 100644
index 00000000..546ff4d2
--- /dev/null
+++ b/Sources/WebUI/Styles/Effects/ViewTransition/DocumentViewTransition.swift
@@ -0,0 +1,436 @@
+import Foundation
+
+/// Configuration for document-level view transitions
+public struct DocumentViewTransitionConfiguration: Sendable {
+ /// The default transition type for the document
+ public let defaultTransition: ViewTransitionType?
+
+ /// The duration for document-level transitions in milliseconds
+ public let duration: Int?
+
+ /// The timing function for document-level transitions
+ public let timing: ViewTransitionTiming?
+
+ /// The delay before transitions start in milliseconds
+ public let delay: Int?
+
+ /// Whether to enable cross-document view transitions
+ public let enableCrossDocument: Bool
+
+ /// Custom CSS for view transitions
+ public let customCSS: String?
+
+ /// Initialize document view transition configuration.
+ ///
+ /// - Parameters:
+ /// - defaultTransition: The default transition type for the document
+ /// - duration: The duration for document-level transitions in milliseconds
+ /// - timing: The timing function for document-level transitions
+ /// - delay: The delay before transitions start in milliseconds
+ /// - enableCrossDocument: Whether to enable cross-document view transitions
+ /// - customCSS: Custom CSS for view transitions
+ public init(
+ defaultTransition: ViewTransitionType? = nil,
+ duration: Int? = nil,
+ timing: ViewTransitionTiming? = nil,
+ delay: Int? = nil,
+ enableCrossDocument: Bool = false,
+ customCSS: String? = nil
+ ) {
+ self.defaultTransition = defaultTransition
+ self.duration = duration
+ self.timing = timing
+ self.delay = delay
+ self.enableCrossDocument = enableCrossDocument
+ self.customCSS = customCSS
+ }
+}
+
+/// Extension to add view transition support to Documents
+extension Document {
+ /// View transition configuration for this document.
+ ///
+ /// Override this property to configure document-level view transitions.
+ /// When specified, generates CSS and JavaScript for smooth page transitions.
+ ///
+ /// - Returns: The view transition configuration, or nil for no document-level transitions
+ public var viewTransitions: DocumentViewTransitionConfiguration? {
+ nil
+ }
+
+ /// Generate CSS for document-level view transitions.
+ ///
+ /// This method generates CSS that enables smooth transitions between pages
+ /// and provides default styling for view transition elements.
+ ///
+ /// - Returns: CSS string for view transitions, or nil if no configuration
+ public func generateViewTransitionCSS() -> String? {
+ guard let config = viewTransitions else { return nil }
+
+ var css = """
+ /* Document-level view transitions */
+ @view-transition {
+ navigation: auto;
+ }
+
+ """
+
+ // Add default transition styles
+ if let defaultTransition = config.defaultTransition {
+ css += """
+ ::view-transition-old(root),
+ ::view-transition-new(root) {
+ animation-duration: \(config.duration ?? 300)ms;
+ animation-timing-function: \(config.timing?.rawValue ?? "ease-in-out");
+ }
+
+ """
+
+ // Add transition-specific styles
+ switch defaultTransition {
+ case .fade:
+ css += """
+ ::view-transition-old(root) {
+ animation-name: fade-out;
+ }
+
+ ::view-transition-new(root) {
+ animation-name: fade-in;
+ }
+
+ @keyframes fade-out {
+ from { opacity: 1; }
+ to { opacity: 0; }
+ }
+
+ @keyframes fade-in {
+ from { opacity: 0; }
+ to { opacity: 1; }
+ }
+
+ """
+ case .slide, .slideLeft:
+ css += """
+ ::view-transition-old(root) {
+ animation-name: slide-out-left;
+ }
+
+ ::view-transition-new(root) {
+ animation-name: slide-in-right;
+ }
+
+ @keyframes slide-out-left {
+ from { transform: translateX(0); }
+ to { transform: translateX(-100%); }
+ }
+
+ @keyframes slide-in-right {
+ from { transform: translateX(100%); }
+ to { transform: translateX(0); }
+ }
+
+ """
+ case .slideRight:
+ css += """
+ ::view-transition-old(root) {
+ animation-name: slide-out-right;
+ }
+
+ ::view-transition-new(root) {
+ animation-name: slide-in-left;
+ }
+
+ @keyframes slide-out-right {
+ from { transform: translateX(0); }
+ to { transform: translateX(100%); }
+ }
+
+ @keyframes slide-in-left {
+ from { transform: translateX(-100%); }
+ to { transform: translateX(0); }
+ }
+
+ """
+ case .slideUp:
+ css += """
+ ::view-transition-old(root) {
+ animation-name: slide-out-up;
+ }
+
+ ::view-transition-new(root) {
+ animation-name: slide-in-down;
+ }
+
+ @keyframes slide-out-up {
+ from { transform: translateY(0); }
+ to { transform: translateY(-100%); }
+ }
+
+ @keyframes slide-in-down {
+ from { transform: translateY(-100%); }
+ to { transform: translateY(0); }
+ }
+
+ """
+ case .slideDown:
+ css += """
+ ::view-transition-old(root) {
+ animation-name: slide-out-down;
+ }
+
+ ::view-transition-new(root) {
+ animation-name: slide-in-up;
+ }
+
+ @keyframes slide-out-down {
+ from { transform: translateY(0); }
+ to { transform: translateY(100%); }
+ }
+
+ @keyframes slide-in-up {
+ from { transform: translateY(100%); }
+ to { transform: translateY(0); }
+ }
+
+ """
+ case .scale, .scaleUp:
+ css += """
+ ::view-transition-old(root) {
+ animation-name: scale-out;
+ }
+
+ ::view-transition-new(root) {
+ animation-name: scale-in;
+ }
+
+ @keyframes scale-out {
+ from { transform: scale(1); }
+ to { transform: scale(1.1); opacity: 0; }
+ }
+
+ @keyframes scale-in {
+ from { transform: scale(0.9); opacity: 0; }
+ to { transform: scale(1); opacity: 1; }
+ }
+
+ """
+ case .scaleDown:
+ css += """
+ ::view-transition-old(root) {
+ animation-name: scale-down-out;
+ }
+
+ ::view-transition-new(root) {
+ animation-name: scale-down-in;
+ }
+
+ @keyframes scale-down-out {
+ from { transform: scale(1); }
+ to { transform: scale(0.9); opacity: 0; }
+ }
+
+ @keyframes scale-down-in {
+ from { transform: scale(1.1); opacity: 0; }
+ to { transform: scale(1); opacity: 1; }
+ }
+
+ """
+ case .flip, .flipHorizontal:
+ css += """
+ ::view-transition-old(root) {
+ animation-name: flip-out;
+ }
+
+ ::view-transition-new(root) {
+ animation-name: flip-in;
+ }
+
+ @keyframes flip-out {
+ from { transform: rotateY(0deg); }
+ to { transform: rotateY(90deg); }
+ }
+
+ @keyframes flip-in {
+ from { transform: rotateY(-90deg); }
+ to { transform: rotateY(0deg); }
+ }
+
+ """
+ case .flipVertical:
+ css += """
+ ::view-transition-old(root) {
+ animation-name: flip-vertical-out;
+ }
+
+ ::view-transition-new(root) {
+ animation-name: flip-vertical-in;
+ }
+
+ @keyframes flip-vertical-out {
+ from { transform: rotateX(0deg); }
+ to { transform: rotateX(90deg); }
+ }
+
+ @keyframes flip-vertical-in {
+ from { transform: rotateX(-90deg); }
+ to { transform: rotateX(0deg); }
+ }
+
+ """
+ case .none:
+ css += """
+ ::view-transition-old(root),
+ ::view-transition-new(root) {
+ animation: none;
+ }
+
+ """
+ }
+ }
+
+ // Add custom CSS if provided
+ if let customCSS = config.customCSS {
+ css += "\n/* Custom view transition styles */\n"
+ css += customCSS
+ css += "\n"
+ }
+
+ return css
+ }
+
+ /// Generate JavaScript for document-level view transitions.
+ ///
+ /// This method generates JavaScript that enables cross-document view transitions
+ /// and provides programmatic control over view transitions.
+ ///
+ /// - Returns: JavaScript string for view transitions, or nil if not enabled
+ public func generateViewTransitionJS() -> String? {
+ guard let config = viewTransitions, config.enableCrossDocument else { return nil }
+
+ return """
+ // Document-level view transitions
+ (function() {
+ 'use strict';
+
+ // Check for View Transitions API support
+ if (!document.startViewTransition) {
+ return;
+ }
+
+ // Enable cross-document view transitions
+ document.addEventListener('DOMContentLoaded', function() {
+ // Handle navigation with view transitions
+ document.addEventListener('click', function(e) {
+ const link = e.target.closest('a');
+ if (link && link.href && link.hostname === window.location.hostname) {
+ e.preventDefault();
+
+ // Start view transition
+ document.startViewTransition(() => {
+ window.location.href = link.href;
+ });
+ }
+ });
+ });
+
+ // Handle back/forward navigation
+ window.addEventListener('popstate', function(e) {
+ document.startViewTransition(() => {
+ window.location.reload();
+ });
+ });
+ })();
+ """
+ }
+}
+
+/// Website extension to provide default view transition configuration
+extension Website {
+ /// Default view transition configuration for all documents in this website.
+ ///
+ /// Override this property to set default view transitions for all pages.
+ /// Individual documents can override this configuration.
+ ///
+ /// - Returns: The default view transition configuration, or nil for no defaults
+ public var defaultViewTransitions: DocumentViewTransitionConfiguration? {
+ nil
+ }
+}
+
+/// Extension to integrate view transitions into the Document rendering pipeline
+extension Document {
+ /// Enhanced head content that includes view transition CSS and JavaScript.
+ ///
+ /// This method combines the original head content with generated view transition
+ /// CSS and JavaScript, providing seamless integration with the existing rendering system.
+ ///
+ /// - Returns: Complete head content including view transitions
+ public var enhancedHead: String? {
+ var headContent = head ?? ""
+
+ // Add view transition CSS
+ if let viewTransitionCSS = generateViewTransitionCSS() {
+ headContent += """
+
+
+ """
+ }
+
+ // Add view transition JavaScript
+ if let viewTransitionJS = generateViewTransitionJS() {
+ headContent += """
+
+
+ """
+ }
+
+ return headContent.isEmpty ? nil : headContent
+ }
+
+ /// Render the document with integrated view transitions.
+ ///
+ /// This method provides an alternative to the standard render() method
+ /// that automatically includes view transition CSS and JavaScript.
+ ///
+ /// - Returns: Complete HTML document with view transitions
+ public func renderWithViewTransitions() throws -> String {
+ var optionalTags: [String] = metadata.tags + []
+ var bodyTags: [String] = []
+ if let scripts = scripts {
+ for script in scripts {
+ let scriptTag = script.render()
+ script.placement == .head
+ ? optionalTags.append(scriptTag)
+ : bodyTags.append(scriptTag)
+ }
+ }
+ if let stylesheets = stylesheets {
+ for stylesheet in stylesheets {
+ optionalTags.append(
+ ""
+ )
+ }
+ }
+ let html = """
+
+
+
+
+
+ \(metadata.pageTitle)
+ \(optionalTags.joined(separator: "\n"))
+
+
+ \(enhancedHead ?? "")
+
+ \(body.render())
+ \(bodyTags.joined(separator: "\n"))
+
+ """
+ return HTMLMinifier.minify(html)
+ }
+}
diff --git a/Sources/WebUI/Styles/Effects/ViewTransition/ViewTransitionStyleOperation.swift b/Sources/WebUI/Styles/Effects/ViewTransition/ViewTransitionStyleOperation.swift
new file mode 100644
index 00000000..4bfde30a
--- /dev/null
+++ b/Sources/WebUI/Styles/Effects/ViewTransition/ViewTransitionStyleOperation.swift
@@ -0,0 +1,406 @@
+import Foundation
+
+/// Style operation for applying CSS View Transitions
+public struct ViewTransitionStyleOperation: StyleOperation, Sendable {
+ public static let shared = ViewTransitionStyleOperation()
+
+ /// Parameters for configuring view transitions
+ public struct Parameters: Sendable {
+ public let transitionType: ViewTransitionType?
+ public let name: String?
+ public let duration: Int?
+ public let timing: ViewTransitionTiming?
+ public let delay: Int?
+ public let slideDirection: SlideDirection?
+ public let scaleOrigin: ScaleOrigin?
+ public let behavior: ViewTransitionBehavior?
+
+ /// Initialize view transition parameters.
+ ///
+ /// - Parameters:
+ /// - transitionType: The type of view transition to apply
+ /// - name: The view transition name for CSS View Transitions API
+ /// - duration: The duration of the transition in milliseconds
+ /// - timing: The timing function for the transition
+ /// - delay: The delay before the transition starts in milliseconds
+ /// - slideDirection: The direction for slide transitions
+ /// - scaleOrigin: The origin point for scale transitions
+ /// - behavior: The transition behavior
+ public init(
+ transitionType: ViewTransitionType? = nil,
+ name: String? = nil,
+ duration: Int? = nil,
+ timing: ViewTransitionTiming? = nil,
+ delay: Int? = nil,
+ slideDirection: SlideDirection? = nil,
+ scaleOrigin: ScaleOrigin? = nil,
+ behavior: ViewTransitionBehavior? = nil
+ ) {
+ self.transitionType = transitionType
+ self.name = name
+ self.duration = duration
+ self.timing = timing
+ self.delay = delay
+ self.slideDirection = slideDirection
+ self.scaleOrigin = scaleOrigin
+ self.behavior = behavior
+ }
+
+ /// Create parameters from StyleParameters.
+ ///
+ /// - Parameter params: The style parameters to convert
+ /// - Returns: A new Parameters instance
+ public static func from(_ params: StyleParameters) -> Parameters {
+ Parameters(
+ transitionType: params.get("viewTransitionType"),
+ name: params.get("viewTransitionName"),
+ duration: params.get("viewTransitionDuration"),
+ timing: params.get("viewTransitionTiming"),
+ delay: params.get("viewTransitionDelay"),
+ slideDirection: params.get("viewTransitionSlideDirection"),
+ scaleOrigin: params.get("viewTransitionScaleOrigin"),
+ behavior: params.get("viewTransitionBehavior")
+ )
+ }
+ }
+
+ /// Apply view transition classes based on parameters.
+ ///
+ /// - Parameter params: The parameters containing view transition configuration
+ /// - Returns: An array of CSS class names to apply
+ public func applyClasses(params: Parameters) -> [String] {
+ var classes: [String] = []
+
+ // Apply view transition name for CSS View Transitions API
+ if let name = params.name {
+ classes.append("view-transition-name-\(name)")
+ }
+
+ // Apply transition type
+ if let transitionType = params.transitionType {
+ classes.append("view-transition-\(transitionType.rawValue)")
+
+ // Apply direction-specific classes for slide transitions
+ if transitionType == .slide, let direction = params.slideDirection {
+ classes.append("view-transition-slide-\(direction.rawValue)")
+ }
+
+ // Apply origin-specific classes for scale transitions
+ if transitionType == .scale || transitionType == .scaleUp || transitionType == .scaleDown,
+ let origin = params.scaleOrigin
+ {
+ classes.append("view-transition-origin-\(origin.rawValue)")
+ }
+ }
+
+ // Apply timing
+ if let timing = params.timing {
+ classes.append("view-transition-timing-\(timing.rawValue)")
+ }
+
+ // Apply duration
+ if let duration = params.duration {
+ classes.append("view-transition-duration-\(duration)")
+ }
+
+ // Apply delay
+ if let delay = params.delay {
+ classes.append("view-transition-delay-\(delay)")
+ }
+
+ // Apply behavior
+ if let behavior = params.behavior {
+ classes.append("view-transition-behavior-\(behavior.rawValue)")
+ }
+
+ return classes
+ }
+
+ private init() {}
+}
+
+
+// MARK: - Markup Extension
+extension Markup {
+ /// Apply view transition configuration to this element.
+ ///
+ /// - Parameters:
+ /// - transitionType: The type of view transition to apply
+ /// - name: The view transition name for CSS View Transitions API
+ /// - duration: The duration of the transition in milliseconds
+ /// - timing: The timing function for the transition
+ /// - delay: The delay before the transition starts in milliseconds
+ /// - slideDirection: The direction for slide transitions
+ /// - scaleOrigin: The origin point for scale transitions
+ /// - behavior: The transition behavior
+ /// - modifiers: The modifiers to apply the transition on
+ /// - Returns: A modified markup element with view transition applied
+ public func viewTransition(
+ _ transitionType: ViewTransitionType? = nil,
+ name: String? = nil,
+ duration: Int? = nil,
+ timing: ViewTransitionTiming? = nil,
+ delay: Int? = nil,
+ slideDirection: SlideDirection? = nil,
+ scaleOrigin: ScaleOrigin? = nil,
+ behavior: ViewTransitionBehavior? = nil,
+ on modifiers: Modifier...
+ ) -> some Markup {
+ let params = ViewTransitionStyleOperation.Parameters(
+ transitionType: transitionType,
+ name: name,
+ duration: duration,
+ timing: timing,
+ delay: delay,
+ slideDirection: slideDirection,
+ scaleOrigin: scaleOrigin,
+ behavior: behavior
+ )
+ return ViewTransitionStyleOperation.shared.applyTo(self, params: params, modifiers: Array(modifiers))
+ }
+
+ /// Apply named view transition for CSS View Transitions API.
+ ///
+ /// - Parameter name: The view transition name
+ /// - Returns: A modified markup element with view transition name applied
+ public func viewTransitionName(_ name: String) -> some Markup {
+ viewTransition(name: name)
+ }
+
+ /// Apply fade transition.
+ ///
+ /// - Parameters:
+ /// - duration: The duration of the transition in milliseconds
+ /// - timing: The timing function for the transition
+ /// - delay: The delay before the transition starts in milliseconds
+ /// - Returns: A modified markup element with fade transition applied
+ public func fadeTransition(duration: Int? = nil, timing: ViewTransitionTiming? = nil, delay: Int? = nil)
+ -> some Markup
+ {
+ viewTransition(.fade, duration: duration, timing: timing, delay: delay)
+ }
+
+ /// Apply slide transition with direction.
+ ///
+ /// - Parameters:
+ /// - direction: The direction for the slide transition
+ /// - duration: The duration of the transition in milliseconds
+ /// - timing: The timing function for the transition
+ /// - delay: The delay before the transition starts in milliseconds
+ /// - Returns: A modified markup element with slide transition applied
+ public func slideTransition(
+ _ direction: SlideDirection,
+ duration: Int? = nil,
+ timing: ViewTransitionTiming? = nil,
+ delay: Int? = nil
+ ) -> some Markup {
+ viewTransition(.slide, duration: duration, timing: timing, delay: delay, slideDirection: direction)
+ }
+
+ /// Apply scale transition with origin.
+ ///
+ /// - Parameters:
+ /// - scaleType: The type of scale transition
+ /// - origin: The origin point for the scale transition
+ /// - duration: The duration of the transition in milliseconds
+ /// - timing: The timing function for the transition
+ /// - delay: The delay before the transition starts in milliseconds
+ /// - Returns: A modified markup element with scale transition applied
+ public func scaleTransition(
+ _ scaleType: ViewTransitionType = .scale,
+ origin: ScaleOrigin? = nil,
+ duration: Int? = nil,
+ timing: ViewTransitionTiming? = nil,
+ delay: Int? = nil
+ ) -> some Markup {
+ viewTransition(scaleType, duration: duration, timing: timing, delay: delay, scaleOrigin: origin)
+ }
+}
+
+// MARK: - ResponsiveBuilder Extension
+extension ResponsiveBuilder {
+ /// Apply view transition configuration in responsive context.
+ ///
+ /// - Parameters:
+ /// - transitionType: The type of view transition to apply
+ /// - name: The view transition name for CSS View Transitions API
+ /// - duration: The duration of the transition in milliseconds
+ /// - timing: The timing function for the transition
+ /// - delay: The delay before the transition starts in milliseconds
+ /// - slideDirection: The direction for slide transitions
+ /// - scaleOrigin: The origin point for scale transitions
+ /// - behavior: The transition behavior
+ /// - Returns: A modified responsive builder with view transition applied
+ public func viewTransition(
+ _ transitionType: ViewTransitionType? = nil,
+ name: String? = nil,
+ duration: Int? = nil,
+ timing: ViewTransitionTiming? = nil,
+ delay: Int? = nil,
+ slideDirection: SlideDirection? = nil,
+ scaleOrigin: ScaleOrigin? = nil,
+ behavior: ViewTransitionBehavior? = nil
+ ) -> ResponsiveBuilder {
+ let params = ViewTransitionStyleOperation.Parameters(
+ transitionType: transitionType,
+ name: name,
+ duration: duration,
+ timing: timing,
+ delay: delay,
+ slideDirection: slideDirection,
+ scaleOrigin: scaleOrigin,
+ behavior: behavior
+ )
+ return ViewTransitionStyleOperation.shared.applyToBuilder(self, params: params)
+ }
+
+ /// Apply named view transition in responsive context.
+ ///
+ /// - Parameter name: The view transition name
+ /// - Returns: A modified responsive builder with view transition name applied
+ public func viewTransitionName(_ name: String) -> ResponsiveBuilder {
+ viewTransition(name: name)
+ }
+
+ /// Apply fade transition in responsive context.
+ ///
+ /// - Parameters:
+ /// - duration: The duration of the transition in milliseconds
+ /// - timing: The timing function for the transition
+ /// - delay: The delay before the transition starts in milliseconds
+ /// - Returns: A modified responsive builder with fade transition applied
+ public func fadeTransition(duration: Int? = nil, timing: ViewTransitionTiming? = nil, delay: Int? = nil)
+ -> ResponsiveBuilder
+ {
+ viewTransition(.fade, duration: duration, timing: timing, delay: delay)
+ }
+
+ /// Apply slide transition in responsive context.
+ ///
+ /// - Parameters:
+ /// - direction: The direction for the slide transition
+ /// - duration: The duration of the transition in milliseconds
+ /// - timing: The timing function for the transition
+ /// - delay: The delay before the transition starts in milliseconds
+ /// - Returns: A modified responsive builder with slide transition applied
+ public func slideTransition(
+ _ direction: SlideDirection,
+ duration: Int? = nil,
+ timing: ViewTransitionTiming? = nil,
+ delay: Int? = nil
+ ) -> ResponsiveBuilder {
+ viewTransition(.slide, duration: duration, timing: timing, delay: delay, slideDirection: direction)
+ }
+
+ /// Apply scale transition in responsive context.
+ ///
+ /// - Parameters:
+ /// - scaleType: The type of scale transition
+ /// - origin: The origin point for the scale transition
+ /// - duration: The duration of the transition in milliseconds
+ /// - timing: The timing function for the transition
+ /// - delay: The delay before the transition starts in milliseconds
+ /// - Returns: A modified responsive builder with scale transition applied
+ public func scaleTransition(
+ _ scaleType: ViewTransitionType = .scale,
+ origin: ScaleOrigin? = nil,
+ duration: Int? = nil,
+ timing: ViewTransitionTiming? = nil,
+ delay: Int? = nil
+ ) -> ResponsiveBuilder {
+ viewTransition(scaleType, duration: duration, timing: timing, delay: delay, scaleOrigin: origin)
+ }
+}
+
+// MARK: - Global DSL Functions
+/// Apply view transition configuration declaratively.
+///
+/// - Parameters:
+/// - transitionType: The type of view transition to apply
+/// - name: The view transition name for CSS View Transitions API
+/// - duration: The duration of the transition in milliseconds
+/// - timing: The timing function for the transition
+/// - delay: The delay before the transition starts in milliseconds
+/// - slideDirection: The direction for slide transitions
+/// - scaleOrigin: The origin point for scale transitions
+/// - behavior: The transition behavior
+/// - Returns: A responsive modification with view transition applied
+public func viewTransition(
+ _ transitionType: ViewTransitionType? = nil,
+ name: String? = nil,
+ duration: Int? = nil,
+ timing: ViewTransitionTiming? = nil,
+ delay: Int? = nil,
+ slideDirection: SlideDirection? = nil,
+ scaleOrigin: ScaleOrigin? = nil,
+ behavior: ViewTransitionBehavior? = nil
+) -> ResponsiveModification {
+ let params = ViewTransitionStyleOperation.Parameters(
+ transitionType: transitionType,
+ name: name,
+ duration: duration,
+ timing: timing,
+ delay: delay,
+ slideDirection: slideDirection,
+ scaleOrigin: scaleOrigin,
+ behavior: behavior
+ )
+ return ViewTransitionStyleOperation.shared.asModification(params: params)
+}
+
+/// Apply named view transition declaratively.
+///
+/// - Parameter name: The view transition name
+/// - Returns: A responsive modification with view transition name applied
+public func viewTransitionName(_ name: String) -> ResponsiveModification {
+ viewTransition(name: name)
+}
+
+/// Apply fade transition declaratively.
+///
+/// - Parameters:
+/// - duration: The duration of the transition in milliseconds
+/// - timing: The timing function for the transition
+/// - delay: The delay before the transition starts in milliseconds
+/// - Returns: A responsive modification with fade transition applied
+public func fadeTransition(duration: Int? = nil, timing: ViewTransitionTiming? = nil, delay: Int? = nil)
+ -> ResponsiveModification
+{
+ viewTransition(.fade, duration: duration, timing: timing, delay: delay)
+}
+
+/// Apply slide transition declaratively.
+///
+/// - Parameters:
+/// - direction: The direction for the slide transition
+/// - duration: The duration of the transition in milliseconds
+/// - timing: The timing function for the transition
+/// - delay: The delay before the transition starts in milliseconds
+/// - Returns: A responsive modification with slide transition applied
+public func slideTransition(
+ _ direction: SlideDirection,
+ duration: Int? = nil,
+ timing: ViewTransitionTiming? = nil,
+ delay: Int? = nil
+) -> ResponsiveModification {
+ viewTransition(.slide, duration: duration, timing: timing, delay: delay, slideDirection: direction)
+}
+
+/// Apply scale transition declaratively.
+///
+/// - Parameters:
+/// - scaleType: The type of scale transition
+/// - origin: The origin point for the scale transition
+/// - duration: The duration of the transition in milliseconds
+/// - timing: The timing function for the transition
+/// - delay: The delay before the transition starts in milliseconds
+/// - Returns: A responsive modification with scale transition applied
+public func scaleTransition(
+ _ scaleType: ViewTransitionType = .scale,
+ origin: ScaleOrigin? = nil,
+ duration: Int? = nil,
+ timing: ViewTransitionTiming? = nil,
+ delay: Int? = nil
+) -> ResponsiveModification {
+ viewTransition(scaleType, duration: duration, timing: timing, delay: delay, scaleOrigin: origin)
+}
diff --git a/Sources/WebUI/Styles/Effects/ViewTransition/ViewTransitionTypes.swift b/Sources/WebUI/Styles/Effects/ViewTransition/ViewTransitionTypes.swift
new file mode 100644
index 00000000..30a0962e
--- /dev/null
+++ b/Sources/WebUI/Styles/Effects/ViewTransition/ViewTransitionTypes.swift
@@ -0,0 +1,60 @@
+import Foundation
+
+/// Specifies the type of view transition to apply
+public enum ViewTransitionType: String, CaseIterable, Sendable {
+ case fade = "fade"
+ case slide = "slide"
+ case slideUp = "slide-up"
+ case slideDown = "slide-down"
+ case slideLeft = "slide-left"
+ case slideRight = "slide-right"
+ case scale = "scale"
+ case scaleUp = "scale-up"
+ case scaleDown = "scale-down"
+ case flip = "flip"
+ case flipHorizontal = "flip-horizontal"
+ case flipVertical = "flip-vertical"
+ case none = "none"
+}
+
+/// Specifies the direction for slide transitions
+public enum SlideDirection: String, CaseIterable, Sendable {
+ case up = "up"
+ case down = "down"
+ case left = "left"
+ case right = "right"
+}
+
+/// Specifies the origin point for scale transitions
+public enum ScaleOrigin: String, CaseIterable, Sendable {
+ case center = "center"
+ case top = "top"
+ case bottom = "bottom"
+ case left = "left"
+ case right = "right"
+ case topLeft = "top-left"
+ case topRight = "top-right"
+ case bottomLeft = "bottom-left"
+ case bottomRight = "bottom-right"
+}
+
+/// Specifies timing function for view transitions
+public enum ViewTransitionTiming: String, CaseIterable, Sendable {
+ case linear = "linear"
+ case easeIn = "ease-in"
+ case easeOut = "ease-out"
+ case easeInOut = "ease-in-out"
+ case circIn = "cubic-bezier(0.55, 0, 1, 0.45)"
+ case circOut = "cubic-bezier(0, 0.55, 0.45, 1)"
+ case circInOut = "cubic-bezier(0.85, 0, 0.15, 1)"
+ case backIn = "cubic-bezier(0.36, 0, 0.66, -0.56)"
+ case backOut = "cubic-bezier(0.34, 1.56, 0.64, 1)"
+ case backInOut = "cubic-bezier(0.68, -0.6, 0.32, 1.6)"
+}
+
+/// Specifies view transition behavior
+public enum ViewTransitionBehavior: String, CaseIterable, Sendable {
+ case auto = "auto"
+ case smooth = "smooth"
+ case instant = "instant"
+}
diff --git a/Tests/WebUITests/Core/DocumentViewTransitionTests.swift b/Tests/WebUITests/Core/DocumentViewTransitionTests.swift
new file mode 100644
index 00000000..800a20f1
--- /dev/null
+++ b/Tests/WebUITests/Core/DocumentViewTransitionTests.swift
@@ -0,0 +1,382 @@
+import Testing
+
+@testable import WebUI
+
+@Suite("Document View Transition Tests")
+struct DocumentViewTransitionTests {
+
+ // MARK: - Test Document Implementations
+
+ struct BasicViewTransitionDocument: Document {
+ var metadata: Metadata {
+ Metadata(
+ title: "Test Page",
+ description: "Test page for view transitions"
+ )
+ }
+
+ var body: some Markup {
+ Stack {
+ Text("Hello World")
+ }
+ }
+
+ var viewTransitions: DocumentViewTransitionConfiguration? {
+ DocumentViewTransitionConfiguration(
+ defaultTransition: .fade,
+ duration: 300,
+ timing: .easeInOut,
+ enableCrossDocument: true
+ )
+ }
+ }
+
+ struct SlideViewTransitionDocument: Document {
+ var metadata: Metadata {
+ Metadata(
+ title: "Slide Page",
+ description: "Test page for slide transitions"
+ )
+ }
+
+ var body: some Markup {
+ Text("Slide Content")
+ }
+
+ var viewTransitions: DocumentViewTransitionConfiguration? {
+ DocumentViewTransitionConfiguration(
+ defaultTransition: .slideLeft,
+ duration: 500,
+ timing: .backOut,
+ delay: 100,
+ enableCrossDocument: true,
+ customCSS: """
+ .custom-transition {
+ animation-delay: 50ms;
+ }
+ """
+ )
+ }
+ }
+
+ struct NoViewTransitionDocument: Document {
+ var metadata: Metadata {
+ Metadata(
+ title: "No Transition Page",
+ description: "Test page without view transitions"
+ )
+ }
+
+ var body: some Markup {
+ Text("No Transition Content")
+ }
+
+ // viewTransitions returns nil by default
+ }
+
+ // MARK: - DocumentViewTransitionConfiguration Tests
+
+ @Test("Document view transition configuration initialization")
+ func testDocumentViewTransitionConfigurationInit() async throws {
+ let config = DocumentViewTransitionConfiguration(
+ defaultTransition: .fade,
+ duration: 300,
+ timing: .easeInOut,
+ delay: 50,
+ enableCrossDocument: true,
+ customCSS: ".custom { color: red; }"
+ )
+
+ #expect(config.defaultTransition == .fade)
+ #expect(config.duration == 300)
+ #expect(config.timing == .easeInOut)
+ #expect(config.delay == 50)
+ #expect(config.enableCrossDocument == true)
+ #expect(config.customCSS == ".custom { color: red; }")
+ }
+
+ @Test("Document view transition configuration with defaults")
+ func testDocumentViewTransitionConfigurationDefaults() async throws {
+ let config = DocumentViewTransitionConfiguration()
+
+ #expect(config.defaultTransition == nil)
+ #expect(config.duration == nil)
+ #expect(config.timing == nil)
+ #expect(config.delay == nil)
+ #expect(config.enableCrossDocument == false)
+ #expect(config.customCSS == nil)
+ }
+
+ // MARK: - CSS Generation Tests
+
+ @Test("Document generates fade transition CSS")
+ func testDocumentGeneratesFadeTransitionCSS() async throws {
+ let document = BasicViewTransitionDocument()
+ let css = document.generateViewTransitionCSS()
+
+ #expect(css != nil)
+ #expect(css!.contains("@view-transition"))
+ #expect(css!.contains("navigation: auto"))
+ #expect(css!.contains("animation-duration: 300ms"))
+ #expect(css!.contains("animation-timing-function: ease-in-out"))
+ #expect(css!.contains("::view-transition-old(root)"))
+ #expect(css!.contains("::view-transition-new(root)"))
+ #expect(css!.contains("animation-name: fade-out"))
+ #expect(css!.contains("animation-name: fade-in"))
+ #expect(css!.contains("@keyframes fade-out"))
+ #expect(css!.contains("@keyframes fade-in"))
+ }
+
+ @Test("Document generates slide transition CSS")
+ func testDocumentGeneratesSlideTransitionCSS() async throws {
+ let document = SlideViewTransitionDocument()
+ let css = document.generateViewTransitionCSS()
+
+ #expect(css != nil)
+ #expect(css!.contains("@view-transition"))
+ #expect(css!.contains("animation-duration: 500ms"))
+ #expect(css!.contains("animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1)"))
+ #expect(css!.contains("animation-name: slide-out-left"))
+ #expect(css!.contains("animation-name: slide-in-right"))
+ #expect(css!.contains("@keyframes slide-out-left"))
+ #expect(css!.contains("@keyframes slide-in-right"))
+ #expect(css!.contains("transform: translateX(-100%)"))
+ #expect(css!.contains("transform: translateX(100%)"))
+ #expect(css!.contains("transform: translateX(0)"))
+ }
+
+ @Test("Document generates custom CSS")
+ func testDocumentGeneratesCustomCSS() async throws {
+ let document = SlideViewTransitionDocument()
+ let css = document.generateViewTransitionCSS()
+
+ #expect(css != nil)
+ #expect(css!.contains("/* Custom view transition styles */"))
+ #expect(css!.contains(".custom-transition"))
+ #expect(css!.contains("animation-delay: 50ms"))
+ }
+
+ @Test("Document with no view transitions generates no CSS")
+ func testDocumentWithNoViewTransitionsGeneratesNoCSS() async throws {
+ let document = NoViewTransitionDocument()
+ let css = document.generateViewTransitionCSS()
+
+ #expect(css == nil)
+ }
+
+ // MARK: - JavaScript Generation Tests
+
+ @Test("Document generates cross-document JavaScript")
+ func testDocumentGeneratesCrossDocumentJavaScript() async throws {
+ let document = BasicViewTransitionDocument()
+ let js = document.generateViewTransitionJS()
+
+ #expect(js != nil)
+ #expect(js!.contains("'use strict'"))
+ #expect(js!.contains("document.startViewTransition"))
+ #expect(js!.contains("DOMContentLoaded"))
+ #expect(js!.contains("addEventListener('click'"))
+ #expect(js!.contains("addEventListener('popstate'"))
+ #expect(js!.contains("window.location.href"))
+ #expect(js!.contains("window.location.reload"))
+ }
+
+ @Test("Document without cross-document transitions generates no JavaScript")
+ func testDocumentWithoutCrossDocumentGeneratesNoJavaScript() async throws {
+ struct NoCrossDocumentTransitionDocument: Document {
+ var metadata: Metadata {
+ Metadata(
+ title: "No Cross Document",
+ description: "Test page"
+ )
+ }
+
+ var body: some Markup {
+ Text("Content")
+ }
+
+ var viewTransitions: DocumentViewTransitionConfiguration? {
+ DocumentViewTransitionConfiguration(
+ defaultTransition: .fade,
+ enableCrossDocument: false
+ )
+ }
+ }
+
+ let document = NoCrossDocumentTransitionDocument()
+ let js = document.generateViewTransitionJS()
+
+ #expect(js == nil)
+ }
+
+ // MARK: - Enhanced Head Content Tests
+
+ @Test("Document enhanced head includes view transition CSS and JavaScript")
+ func testDocumentEnhancedHeadIncludesViewTransitions() async throws {
+ let document = BasicViewTransitionDocument()
+ let enhancedHead = document.enhancedHead
+
+ #expect(enhancedHead != nil)
+ #expect(enhancedHead!.contains(""))
+ #expect(enhancedHead!.contains(""))
+ }
+
+ @Test("Document enhanced head with custom head content")
+ func testDocumentEnhancedHeadWithCustomContent() async throws {
+ struct CustomHeadDocument: Document {
+ var metadata: Metadata {
+ Metadata(
+ title: "Custom Head",
+ description: "Test page"
+ )
+ }
+
+ var body: some Markup {
+ Text("Content")
+ }
+
+ var head: String? {
+ ""
+ }
+
+ var viewTransitions: DocumentViewTransitionConfiguration? {
+ DocumentViewTransitionConfiguration(
+ defaultTransition: .fade,
+ enableCrossDocument: true
+ )
+ }
+ }
+
+ let document = CustomHeadDocument()
+ let enhancedHead = document.enhancedHead
+
+ #expect(enhancedHead != nil)
+ #expect(enhancedHead!.contains(""))
+ #expect(enhancedHead!.contains("