Type-safe media query support for responsive design in the Summon framework. Provides declarative helpers for applying styles based on viewport dimensions, device characteristics, and user preferences.
Package: code.yousef.summon.modifier Since: 0.4.8.4
All media query types extend the sealed class MediaQuery:
sealed class MediaQuery {
abstract override fun toString(): String
}
Applies styles when viewport width is at least the specified value.
data class MinWidth(val pixels: Int) : MediaQuery()
Example:
MediaQuery.MinWidth(768) // (min-width: 768px)
Applies styles when viewport width is at most the specified value.
data class MaxWidth(val pixels: Int) : MediaQuery()
Example:
MediaQuery.MaxWidth(1024) // (max-width: 1024px)
Applies styles when viewport height is at least the specified value.
data class MinHeight(val pixels: Int) : MediaQuery()
Example:
MediaQuery.MinHeight(600) // (min-height: 600px)
Applies styles when viewport height is at most the specified value.
data class MaxHeight(val pixels: Int) : MediaQuery()
Example:
MediaQuery.MaxHeight(900) // (max-height: 900px)
Applies styles when device is in portrait mode (height >= width).
object Portrait : MediaQuery()
Example:
MediaQuery.Portrait // (orientation: portrait)
Applies styles when device is in landscape mode (width > height).
object Landscape : MediaQuery()
Example:
MediaQuery.Landscape // (orientation: landscape)
Applies styles when user prefers dark color scheme.
object PrefersDarkScheme : MediaQuery()
Example:
MediaQuery.PrefersDarkScheme // (prefers-color-scheme: dark)
Applies styles when user prefers light color scheme.
object PrefersLightScheme : MediaQuery()
Example:
MediaQuery.PrefersLightScheme // (prefers-color-scheme: light)
Applies styles when user prefers reduced motion.
object PrefersReducedMotion : MediaQuery()
Example:
MediaQuery.PrefersReducedMotion // (prefers-reduced-motion: reduce)
Applies styles on devices that support hover (typically desktops).
object CanHover : MediaQuery()
Example:
MediaQuery.CanHover // (hover: hover)
Applies styles on devices without hover support (typically touch devices).
object NoHover : MediaQuery()
Example:
MediaQuery.NoHover // (hover: none)
Applies styles on devices with precise pointer input (mouse).
object FinePointer : MediaQuery()
Example:
MediaQuery.FinePointer // (pointer: fine)
Applies styles on devices with imprecise pointer input (touch screens).
object CoarsePointer : MediaQuery()
Example:
MediaQuery.CoarsePointer // (pointer: coarse)
Combines multiple queries with AND logic (all must be true).
data class And(val queries: List<MediaQuery>) : MediaQuery() {
constructor(vararg queries: MediaQuery) : this(queries.toList())
}
Example:
MediaQuery.And(
MediaQuery.MinWidth(768),
MediaQuery.Portrait
)
// (min-width: 768px) and (orientation: portrait)
Combines multiple queries with OR logic (at least one must be true).
data class Or(val queries: List<MediaQuery>) : MediaQuery() {
constructor(vararg queries: MediaQuery) : this(queries.toList())
}
Example:
MediaQuery.Or(
MediaQuery.MinWidth(1024),
MediaQuery.Landscape
)
// (min-width: 1024px), (orientation: landscape)
Custom media query for advanced use cases.
data class Custom(val query: String) : MediaQuery()
Example:
MediaQuery.Custom("(aspect-ratio: 16/9)")
Applies styles conditionally based on a media query.
Signature:
fun Modifier.mediaQuery(
query: MediaQuery,
builder: Modifier.() -> Modifier
): Modifier
Parameters:
query: MediaQuery - The media query conditionbuilder: Modifier.() -> Modifier - DSL builder for styles to apply when query matchesReturns: A new Modifier with media query styles
Example:
Box(modifier = Modifier()
.padding("8px")
.mediaQuery(MediaQuery.MinWidth(768)) {
padding("16px")
}
.mediaQuery(MediaQuery.MinWidth(1024)) {
padding("24px")
}
)
Predefined breakpoint constants for common screen sizes.
object Breakpoints {
/** Small mobile devices (320px) */
val XS = 320
/** Mobile devices (640px) */
val SM = 640
/** Tablets (768px) */
val MD = 768
/** Small desktops (1024px) */
val LG = 1024
/** Large desktops (1280px) */
val XL = 1280
/** Extra large screens (1536px) */
val XXL = 1536
}
Usage:
Box(modifier = Modifier()
.fontSize("14px")
.mediaQuery(MediaQuery.MinWidth(Breakpoints.MD)) {
fontSize("16px")
}
.mediaQuery(MediaQuery.MinWidth(Breakpoints.LG)) {
fontSize("18px")
}
)
Box(modifier = Modifier()
.padding("8px") // Mobile
.mediaQuery(MediaQuery.MinWidth(Breakpoints.MD)) {
padding("16px") // Tablet
}
.mediaQuery(MediaQuery.MinWidth(Breakpoints.LG)) {
padding("24px") // Desktop
}
)
Box(modifier = Modifier()
.backgroundColor("#ffffff")
.color("#000000")
.mediaQuery(MediaQuery.PrefersDarkScheme) {
backgroundColor("#1a1a1a")
color("#ffffff")
}
)
Box(modifier = Modifier()
.transition("all", duration = 300)
.mediaQuery(MediaQuery.PrefersReducedMotion) {
transition("none")
}
)
Button(
modifier = Modifier()
.padding("8px 16px")
.mediaQuery(MediaQuery.NoHover) {
padding("12px 20px") // Larger touch target
}
.mediaQuery(MediaQuery.CanHover) {
cursor("pointer")
}
)
Box(modifier = Modifier()
.width("100%")
.mediaQuery(
MediaQuery.And(
MediaQuery.MinWidth(768),
MediaQuery.Landscape
)
) {
width("50%")
}
)
// Start with mobile styles, enhance for larger screens
Box(modifier = Modifier()
.fontSize("14px")
.lineHeight("1.5")
.padding("16px")
// Tablet
.mediaQuery(MediaQuery.MinWidth(Breakpoints.MD)) {
fontSize("16px")
padding("24px")
}
// Desktop
.mediaQuery(MediaQuery.MinWidth(Breakpoints.LG)) {
fontSize("18px")
lineHeight("1.6")
padding("32px")
}
)
// Start with desktop styles, override for smaller screens
Box(modifier = Modifier()
.fontSize("18px")
.padding("32px")
// Tablet
.mediaQuery(MediaQuery.MaxWidth(Breakpoints.LG)) {
fontSize("16px")
padding("24px")
}
// Mobile
.mediaQuery(MediaQuery.MaxWidth(Breakpoints.MD)) {
fontSize("14px")
padding("16px")
}
)
@media rules with unique class names<head>// This code:
Box(modifier = Modifier()
.padding("8px")
.mediaQuery(MediaQuery.MinWidth(768)) {
padding("16px")
}
)
// Generates CSS like:
@media (min-width: 768px) {
.media-abc123 {
padding: 16px;
}
}
// Mobile-first (recommended)
Modifier()
.fontSize("14px")
.mediaQuery(MediaQuery.MinWidth(768)) { fontSize("16px") }
// Desktop-first
Modifier()
.fontSize("18px")
.mediaQuery(MediaQuery.MaxWidth(768)) { fontSize("14px") }
// Good
.mediaQuery(MediaQuery.MinWidth(Breakpoints.MD)) { }
// Avoid magic numbers
.mediaQuery(MediaQuery.MinWidth(768)) { }
// Good
.mediaQuery(MediaQuery.MinWidth(Breakpoints.MD)) {
padding("16px")
fontSize("16px")
lineHeight("1.6")
}
// Avoid
.mediaQuery(MediaQuery.MinWidth(Breakpoints.MD)) { padding("16px") }
.mediaQuery(MediaQuery.MinWidth(Breakpoints.MD)) { fontSize("16px") }
.mediaQuery(MediaQuery.MinWidth(Breakpoints.MD)) { lineHeight("1.6") }
// Always provide fallbacks for dark mode
.backgroundColor("#ffffff")
.mediaQuery(MediaQuery.PrefersDarkScheme) {
backgroundColor("#1a1a1a")
}
// Respect reduced motion
.transition("all", 300)
.mediaQuery(MediaQuery.PrefersReducedMotion) {
transition("none")
}
@media rule| Query Type | Support | |-------------------|---------------------------------------| | Width/Height | All modern browsers | | Orientation | All modern browsers | | Dark/Light Scheme | Chrome 76+, Safari 12.1+, Firefox 67+ | | Reduced Motion | Chrome 74+, Safari 10.1+, Firefox 63+ | | Hover | Chrome 38+, Safari 9+, Firefox 64+ | | Pointer | Chrome 41+, Safari 9+, Firefox 64+ |