replica: one folder per app (replica/wetter)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
// Generated from tokens.json by gen_theme.py. Do not edit by hand.
|
||||
package app.wetter.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
object WColor {
|
||||
val bg = Color(0xFFF8F9FF)
|
||||
val surface = Color(0xFFFFFFFF)
|
||||
val surfaceContainer = Color(0xFFECEEF4)
|
||||
val surfaceContainerHigh = Color(0xFFE1E3EA)
|
||||
val border = Color(0xFFC3C6CF)
|
||||
val borderInput = Color(0xFF73777F)
|
||||
val text = Color(0xFF191C20)
|
||||
val textMuted = Color(0xFF43474E)
|
||||
val accent = Color(0xFF2A5EA8)
|
||||
val onAccent = Color(0xFFFFFFFF)
|
||||
val accentContainer = Color(0xFFD6E3FF)
|
||||
val onAccentContainer = Color(0xFF001B3E)
|
||||
val danger = Color(0xFFBA1A1A)
|
||||
val warning = Color(0xFF8A5100)
|
||||
val success = Color(0xFF2E6B30)
|
||||
val darkBg = Color(0xFF111318)
|
||||
val darkSurface = Color(0xFF1D2024)
|
||||
val darkSurfaceContainer = Color(0xFF272A2F)
|
||||
val darkBorder = Color(0xFF43474E)
|
||||
val darkBorderInput = Color(0xFF8D9199)
|
||||
val darkText = Color(0xFFE2E2E9)
|
||||
val darkTextMuted = Color(0xFFC3C6CF)
|
||||
val darkAccent = Color(0xFFA9C7FF)
|
||||
val darkOnAccent = Color(0xFF003063)
|
||||
val darkDanger = Color(0xFFFFB4AB)
|
||||
val darkWarning = Color(0xFFFFB870)
|
||||
val sceneClearDayTop = Color(0xFF7CC4EF)
|
||||
val sceneClearDayBottom = Color(0xFFBFE3F7)
|
||||
val sceneClearNightTop = Color(0xFF3D4F9A)
|
||||
val sceneClearNightBottom = Color(0xFF5B4F8F)
|
||||
val sceneCloudyDayTop = Color(0xFFC9CDD2)
|
||||
val sceneCloudyDayBottom = Color(0xFFDFE2E5)
|
||||
val sceneRainTop = Color(0xFF2E3D4F)
|
||||
val sceneRainBottom = Color(0xFF5A6B7D)
|
||||
val sceneSnowTop = Color(0xFF7C8EA3)
|
||||
val sceneSnowBottom = Color(0xFFD4DDE6)
|
||||
val sceneStormTop = Color(0xFF1F1A33)
|
||||
val sceneStormBottom = Color(0xFF4A3F63)
|
||||
val sceneFogTop = Color(0xFF8A929B)
|
||||
val sceneFogBottom = Color(0xFFC4C9CE)
|
||||
val onScene = Color(0xFFFFFFFF)
|
||||
val onSceneMuted = Color(0xFFE8EDF3)
|
||||
val onSceneDark = Color(0xFF14181D)
|
||||
val sceneCard = Color(0xFF2A3644)
|
||||
val sceneCardLight = Color(0xFFF2F5F8)
|
||||
val alertMinor = Color(0xFFFFF2B8)
|
||||
val onAlertMinor = Color(0xFF3D2F00)
|
||||
val alertSevere = Color(0xFFFFDBC8)
|
||||
val onAlertSevere = Color(0xFF4A1A00)
|
||||
val alertExtreme = Color(0xFF93000A)
|
||||
val onAlertExtreme = Color(0xFFFFFFFF)
|
||||
val aqiGood = Color(0xFF2E7D32)
|
||||
val aqiModerate = Color(0xFFA06A00)
|
||||
val aqiBad = Color(0xFFC62828)
|
||||
val uvHigh = Color(0xFFB34700)
|
||||
val card = Color(0xFFFDF8F3)
|
||||
val onCard = Color(0xFF1F1B17)
|
||||
val onCardMuted = Color(0xFF5A524B)
|
||||
val darkCard = Color(0xFF1F1714)
|
||||
val darkOnCard = Color(0xFFEDE0DB)
|
||||
val darkOnCardMuted = Color(0xFFC4B6B0)
|
||||
val fillPrimary = Color(0xFFD3E3FD)
|
||||
val onFillPrimary = Color(0xFF0B2A55)
|
||||
val fillSecondary = Color(0xFFE9DDFF)
|
||||
val onFillSecondary = Color(0xFF2A1A4D)
|
||||
val fillTertiary = Color(0xFFC8E6C2)
|
||||
val onFillTertiary = Color(0xFF0F3A12)
|
||||
val darkFillPrimary = Color(0xFFA8C7FA)
|
||||
val darkOnFillPrimary = Color(0xFF062E6F)
|
||||
val darkFillTertiary = Color(0xFF2D5A27)
|
||||
val darkOnFillTertiary = Color(0xFFB7F0AC)
|
||||
val darkFillOlive = Color(0xFF3E4A1E)
|
||||
val pill = Color(0xFFF1EBE5)
|
||||
val darkPill = Color(0xFF2B221E)
|
||||
val pillSelected = Color(0xFFA8C7FA)
|
||||
val onPillSelected = Color(0xFF0B2A55)
|
||||
val nowChip = Color(0xFF4E4038)
|
||||
val onNowChip = Color(0xFFFFFFFF)
|
||||
val precipText = Color(0xFF2A5EA8)
|
||||
val darkPrecipText = Color(0xFFA8C7FA)
|
||||
val nowcastDot = Color(0xFF2E7D32)
|
||||
val iconSun = Color(0xFFFFB02E)
|
||||
val iconSunCore = Color(0xFFFF9A1F)
|
||||
val iconMoon = Color(0xFFA9C4FF)
|
||||
val iconCloud = Color(0xFFF4F6F9)
|
||||
val iconCloudShade = Color(0xFFCFD6DF)
|
||||
val iconCloudDark = Color(0xFF9AA5B3)
|
||||
val iconDrop = Color(0xFF3D7BF0)
|
||||
val iconSnow = Color(0xFFFFFFFF)
|
||||
val iconSnowOutline = Color(0xFF9FB3CC)
|
||||
val iconBolt = Color(0xFFFFC93C)
|
||||
val iconFog = Color(0xFFC3CAD3)
|
||||
val sceneGlowSun = Color(0xFFFFF3C4)
|
||||
val sceneCloud = Color(0xFFFFFFFF)
|
||||
val sceneHill = Color(0xFF9FC5E6)
|
||||
val darkSceneHill = Color(0xFF2F3F78)
|
||||
val rangeCold = Color(0xFF6FA8FF)
|
||||
val rangeWarm = Color(0xFFFFB02E)
|
||||
val aqiBar1 = Color(0xFF4FD1C5)
|
||||
val aqiBar2 = Color(0xFF9CCC65)
|
||||
val aqiBar3 = Color(0xFFFFD54F)
|
||||
val aqiBar4 = Color(0xFFFF8A65)
|
||||
val aqiBar5 = Color(0xFFE53935)
|
||||
val aqiBar6 = Color(0xFF8E24AA)
|
||||
val uvDotLow = Color(0xFF5CCF8A)
|
||||
val uvDotOff = Color(0xFF6B5A52)
|
||||
val widgetOceanLightBg = Color(0xFFCCE4F0)
|
||||
val widgetOceanLightOn = Color(0xFF0F2733)
|
||||
val widgetOceanLightAccent = Color(0xFF20546F)
|
||||
val widgetOceanLightPanel = Color(0xFFF9FAFB)
|
||||
val widgetOceanLightOnpanel = Color(0xFF192124)
|
||||
val widgetOceanLightBadge = Color(0xFFA8D1E6)
|
||||
val widgetOceanLightOnbadge = Color(0xFF0F2733)
|
||||
val widgetOceanDarkBg = Color(0xFF194257)
|
||||
val widgetOceanDarkOn = Color(0xFFDBECF5)
|
||||
val widgetOceanDarkAccent = Color(0xFFB0D5E8)
|
||||
val widgetOceanDarkPanel = Color(0xFF0E1315)
|
||||
val widgetOceanDarkOnpanel = Color(0xFFE7ECEF)
|
||||
val widgetOceanDarkBadge = Color(0xFF1F516B)
|
||||
val widgetOceanDarkOnbadge = Color(0xFFDFEEF6)
|
||||
val widgetForestLightBg = Color(0xFFD2E9DA)
|
||||
val widgetForestLightOn = Color(0xFF162D1D)
|
||||
val widgetForestLightAccent = Color(0xFF2E603F)
|
||||
val widgetForestLightPanel = Color(0xFFF9FBFA)
|
||||
val widgetForestLightOnpanel = Color(0xFF1B221D)
|
||||
val widgetForestLightBadge = Color(0xFFB3DBC0)
|
||||
val widgetForestLightOnbadge = Color(0xFF162D1D)
|
||||
val widgetForestDarkBg = Color(0xFF244C32)
|
||||
val widgetForestDarkOn = Color(0xFFE0F0E5)
|
||||
val widgetForestDarkAccent = Color(0xFFBADEC6)
|
||||
val widgetForestDarkPanel = Color(0xFF101411)
|
||||
val widgetForestDarkOnpanel = Color(0xFFE8EDEA)
|
||||
val widgetForestDarkBadge = Color(0xFF2D5D3D)
|
||||
val widgetForestDarkOnbadge = Color(0xFFE3F2E8)
|
||||
val widgetSunLightBg = Color(0xFFF7E4C5)
|
||||
val widgetSunLightOn = Color(0xFF3A2808)
|
||||
val widgetSunLightAccent = Color(0xFF7D5612)
|
||||
val widgetSunLightPanel = Color(0xFFFBFAF9)
|
||||
val widgetSunLightOnpanel = Color(0xFF272117)
|
||||
val widgetSunLightBadge = Color(0xFFF1D29D)
|
||||
val widgetSunLightOnbadge = Color(0xFF3A2808)
|
||||
val widgetSunDarkBg = Color(0xFF62430E)
|
||||
val widgetSunDarkOn = Color(0xFFF9EDD7)
|
||||
val widgetSunDarkAccent = Color(0xFFF2D6A6)
|
||||
val widgetSunDarkPanel = Color(0xFF17130D)
|
||||
val widgetSunDarkOnpanel = Color(0xFFF0ECE5)
|
||||
val widgetSunDarkBadge = Color(0xFF785311)
|
||||
val widgetSunDarkOnbadge = Color(0xFFFAEFDB)
|
||||
val widgetLavenderLightBg = Color(0xFFDBCFED)
|
||||
val widgetLavenderLightOn = Color(0xFF1F1230)
|
||||
val widgetLavenderLightAccent = Color(0xFF422768)
|
||||
val widgetLavenderLightPanel = Color(0xFFFAF9FB)
|
||||
val widgetLavenderLightOnpanel = Color(0xFF1E1A23)
|
||||
val widgetLavenderLightBadge = Color(0xFFC3AEE0)
|
||||
val widgetLavenderLightOnbadge = Color(0xFF1F1230)
|
||||
val widgetLavenderDarkBg = Color(0xFF341F51)
|
||||
val widgetLavenderDarkOn = Color(0xFFE6DEF2)
|
||||
val widgetLavenderDarkAccent = Color(0xFFC8B5E3)
|
||||
val widgetLavenderDarkPanel = Color(0xFF110F15)
|
||||
val widgetLavenderDarkOnpanel = Color(0xFFEAE7EE)
|
||||
val widgetLavenderDarkBadge = Color(0xFF402664)
|
||||
val widgetLavenderDarkOnbadge = Color(0xFFE9E1F4)
|
||||
val widgetGraphiteLightBg = Color(0xFFDBDDE1)
|
||||
val widgetGraphiteLightOn = Color(0xFF1E2124)
|
||||
val widgetGraphiteLightAccent = Color(0xFF42464D)
|
||||
val widgetGraphiteLightPanel = Color(0xFFFAFAFA)
|
||||
val widgetGraphiteLightOnpanel = Color(0xFF1E1E1F)
|
||||
val widgetGraphiteLightBadge = Color(0xFFC2C6CB)
|
||||
val widgetGraphiteLightOnbadge = Color(0xFF1E2124)
|
||||
val widgetGraphiteDarkBg = Color(0xFF34373D)
|
||||
val widgetGraphiteDarkOn = Color(0xFFE6E8EA)
|
||||
val widgetGraphiteDarkAccent = Color(0xFFC8CBD0)
|
||||
val widgetGraphiteDarkPanel = Color(0xFF111212)
|
||||
val widgetGraphiteDarkOnpanel = Color(0xFFEAEBEB)
|
||||
val widgetGraphiteDarkBadge = Color(0xFF3F444A)
|
||||
val widgetGraphiteDarkOnbadge = Color(0xFFE9EAEC)
|
||||
val darkAccentContainer = Color(0xFF244A80)
|
||||
val darkOnAccentContainer = Color(0xFFD6E3FF)
|
||||
}
|
||||
|
||||
object WType {
|
||||
val labelSm = TextStyle(fontSize = 11.sp, lineHeight = 16.sp, fontWeight = FontWeight.W500)
|
||||
val label = TextStyle(fontSize = 12.sp, lineHeight = 16.sp, fontWeight = FontWeight.W500)
|
||||
val bodySm = TextStyle(fontSize = 14.sp, lineHeight = 20.sp, fontWeight = FontWeight.W400)
|
||||
val body = TextStyle(fontSize = 16.sp, lineHeight = 24.sp, fontWeight = FontWeight.W400)
|
||||
val title = TextStyle(fontSize = 18.sp, lineHeight = 24.sp, fontWeight = FontWeight.W500)
|
||||
val headline = TextStyle(fontSize = 24.sp, lineHeight = 32.sp, fontWeight = FontWeight.W500)
|
||||
val metric = TextStyle(fontSize = 32.sp, lineHeight = 40.sp, fontWeight = FontWeight.W500)
|
||||
val display = TextStyle(fontSize = 112.sp, lineHeight = 112.sp, fontWeight = FontWeight.W700)
|
||||
val displayWidget = TextStyle(fontSize = 44.sp, lineHeight = 48.sp, fontWeight = FontWeight.W400)
|
||||
val dayRange = TextStyle(fontSize = 56.sp, lineHeight = 60.sp, fontWeight = FontWeight.W600)
|
||||
val heroCondition = TextStyle(fontSize = 22.sp, lineHeight = 28.sp, fontWeight = FontWeight.W400)
|
||||
val cardTitle = TextStyle(fontSize = 16.sp, lineHeight = 24.sp, fontWeight = FontWeight.W500)
|
||||
}
|
||||
|
||||
object WSpace {
|
||||
val s0 = 0.dp
|
||||
val s2 = 2.dp
|
||||
val s4 = 4.dp
|
||||
val s8 = 8.dp
|
||||
val s12 = 12.dp
|
||||
val s16 = 16.dp
|
||||
val s20 = 20.dp
|
||||
val s24 = 24.dp
|
||||
val s32 = 32.dp
|
||||
val s48 = 48.dp
|
||||
}
|
||||
|
||||
object WRadius {
|
||||
val xs = 8.dp
|
||||
val sm = 12.dp
|
||||
val md = 16.dp
|
||||
val lg = 24.dp
|
||||
val xl = 28.dp
|
||||
val widget = 24.dp
|
||||
val pill = 999.dp
|
||||
val card = 28.dp
|
||||
val dayPill = 24.dp
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
# Komponenten: Wetter-Klon (Compose, Material 3 Expressive)
|
||||
|
||||
Tokens: `tokens.json` → `Tokens.kt` (über `gen_theme.py`). Komponenten verwenden nur Rollen (`WColor.onScene`), nie rohe Hex-Werte.
|
||||
Icons: Material Symbols Rounded (Apache 2.0) für die UI. Die Wettericons sind eigene oder [Meteocons](https://github.com/basmilius/weather-icons) (MIT), animiert über Lottie. Kein Google-Artwork.
|
||||
Schrift: Roboto Flex (OFL). Texte sind alle neu geschrieben, auf Deutsch.
|
||||
|
||||
## Grundregel Kontrast über Szenen
|
||||
|
||||
Text auf dem animierten Himmel nutzt `on-scene`, bei hellen Szenen (Schnee, Nebel, unterer Tagesverlauf) `on-scene-dark`. Die Szene meldet ihre Helligkeit (`scene.isLight`), und das Theme wählt danach.
|
||||
Beim klaren Tageshimmel oben besteht weißer Text nur als Großtext (3,9:1). Kleiner Text dort braucht deshalb einen Scrim, einen Verlauf von 0 auf 25 % Schwarz hinter dem Hero-Kopf.
|
||||
Karten auf der Szene: `scene-card` mit 85 % Deckkraft plus Blur (API 31+, `RenderEffect`). Ohne Blur wird `scene-card` voll deckend gezeigt.
|
||||
|
||||
---
|
||||
|
||||
```
|
||||
WeatherScene (animierter Hintergrund)
|
||||
variants clear, partly-cloudy, cloudy, rain, drizzle, snow, sleet, storm, fog × day/night; intensity 0..1
|
||||
layers sky gradient (scene-*-top/bottom) → sun/moon+stars → clouds (AGSL noise) → precipitation particles → lightning flash → fog
|
||||
states playing, paused (off-screen / lifecycle STOPPED), reduced (battery saver, "Animationen entfernen", setting off → static gradient), transitioning (crossfade 600ms between conditions)
|
||||
motion parallax 0.3× on scroll, wind direction tilts rain, haptic tick on lightning (if vibrations on, never during severe alert)
|
||||
a11y decorative: no semantics; condition always also stated in text
|
||||
perf adaptive quality (particle count by frame time), 30fps cap on low-end
|
||||
used on S01, S02 (row backgrounds, static), S12 (pre-rendered bitmap)
|
||||
|
||||
HeroBlock
|
||||
content city name (title) + location pin if current, temperature (display 96sp, wdth 125), condition (headline), "Gefühlt 12°" + H/T (body)
|
||||
states loading (shimmer on numbers, scene neutral), filled, stale ("Stand: vor 2 Std" chip), offline (cloud-off icon chip), no-permission (CTA "Standort erlauben")
|
||||
tokens on-scene / on-scene-dark, space 24 top
|
||||
motion temperature counts up on first load (300ms), collapses into app bar on scroll (large → small top app bar)
|
||||
a11y one merged semantics node: "Berlin, 14 Grad, leicht bewölkt, gefühlt 12, Höchstwert 17, Tiefstwert 9"
|
||||
used on S01
|
||||
|
||||
AlertBanner
|
||||
variants minor (alert-minor), moderate/severe (alert-severe), extreme (alert-extreme)
|
||||
content icon, event ("Sturmböen"), agency + validity ("DWD · bis 18:00"), chevron
|
||||
states single, stacked (n more), expanded (inline first lines), expired (removed with fade)
|
||||
tokens radius lg, padding 16
|
||||
a11y liveRegion polite on appear; whole banner is one button "Warnung: Sturmböen, Deutscher Wetterdienst, bis 18 Uhr. Details"
|
||||
used on S01 (top of cards, replaces Brief), S11
|
||||
|
||||
BriefCard
|
||||
content 1–2 sentences, generated (e.g. "Ab 15 Uhr Regen, abends trocken. Morgen 6° kühler.")
|
||||
states loading (two shimmer lines), filled, hidden (alert active or setting off)
|
||||
tokens scene-card, body, radius xl
|
||||
used on S01
|
||||
|
||||
HourlyStrip
|
||||
content per hour: time label, icon, temp, precip % if ≥ 20 %; sunrise/sunset inserted as items; "Jetzt" first
|
||||
states loading, filled, scroll; selected-hour (tap shows detail tooltip)
|
||||
extra temperature line chart behind items (Canvas, smooth cubic)
|
||||
a11y LazyRow, each item "15 Uhr, 14 Grad, Regen 40 Prozent"
|
||||
used on S01 (fixed position), S04, S12 (widget variant)
|
||||
|
||||
DayRow / DailyList
|
||||
content weekday ("Heute", "Mo"), icon, precip %, low temp, range bar (gradient cold→warm over the 10-day min..max, dot = current temp today), high temp
|
||||
states default, pressed (ripple), expanded (inline hourly, optional)
|
||||
a11y "Montag, Regen 60 Prozent, 8 bis 15 Grad"
|
||||
used on S01, S12
|
||||
|
||||
MetricCard (base for all detail cards)
|
||||
variants small 1×1, wide 2×1; shapes per tokens.shape (squircle, cookie, arch, pill)
|
||||
slots header (icon + label), value (metric 32sp), visual (gauge/chart), footer (one-line explanation)
|
||||
states default, pressed (morph radius→md, spring-fast), dragging (elevation card-drag, scale 1.04, haptic), placeholder (dashed border while dragging), loading, unavailable ("In deiner Region nicht verfügbar")
|
||||
tokens scene-card / scene-card-light, on-scene, radius lg
|
||||
a11y button → opens S05; reorder via accessibility actions "Nach oben/unten verschieben"
|
||||
used on S01, S10
|
||||
|
||||
instances
|
||||
Precip next-hour bar chart (minutely_15) + "Regen in ~20 Min" / total today
|
||||
Wind compass dial with arrow, speed, gusts, Beaufort label
|
||||
UV cookie shape, arc 0–11+, colour by band (uv-high etc.), "Sonnenschutz 11–16 Uhr"
|
||||
Humidity value + dew point, vertical fill
|
||||
Pressure dial with trend arrow (rising/falling over 3h)
|
||||
Visibility value + description
|
||||
Sun arch with sun position dot, sunrise/sunset times, day length
|
||||
AQI pill scale (aqi-good..aqi-bad), value, main pollutant
|
||||
Pollen top 3 types with level dots (Europe only, else hidden)
|
||||
Map static radar preview thumbnail → opens S06
|
||||
|
||||
RadarMap (S06)
|
||||
content MapLibre + OSM base (light/dark style), RainViewer overlay, location dot, attribution
|
||||
controls timeline scrubber (past 2h … +30min), play/pause FAB, time label, legend
|
||||
states loading tiles, playing, paused, no-coverage, offline
|
||||
a11y scrubber is a Slider with "13:40, Vorhersage" values
|
||||
used on S06
|
||||
|
||||
CityRow
|
||||
content name, admin area, local time, temp, condition icon, H/T; current-location row with pin and not deletable
|
||||
states default, pressed, swiping (red bg + trash icon reveal), deleted (snackbar "Gelöscht · Rückgängig"), dragging (reorder)
|
||||
bg static mini scene gradient for the city's condition
|
||||
motion shared element (name + temp) into S01
|
||||
used on S02
|
||||
|
||||
SearchField + ResultList
|
||||
states empty (recents), typing (debounced 300ms, progress), results, no results ("Kein Ort gefunden"), error (retry)
|
||||
a11y SearchBar (M3), results announce count
|
||||
used on S03
|
||||
|
||||
TopAppBar
|
||||
variants transparent over scene (S01), surface (S02, S08)
|
||||
content menu/list button, city title (collapsed), settings icon (no account avatar)
|
||||
used on S01, S02, S08
|
||||
|
||||
PagerIndicator
|
||||
dots for saved cities, current one wide (expressive pill); swipe between cities
|
||||
used on S01
|
||||
|
||||
Settings rows
|
||||
variants radio group, switch, link row, section header
|
||||
states default, disabled (with reason text), focused
|
||||
used on S08, S09
|
||||
|
||||
Snackbar, Dialog (permission rationale), Shimmer placeholder: M3 standard with tokens.
|
||||
```
|
||||
|
||||
## Widgets (Glance)
|
||||
|
||||
```
|
||||
Widget shell
|
||||
radius widget (24dp, follows system launcher radius on Android 12+)
|
||||
styles dynamic_color (surface-container from wallpaper), condition (pre-rendered scene bitmap + on-scene text), transparent (text with shadow)
|
||||
states loading ("…"), filled, stale (small clock icon), no location ("Ort wählen"), error (tap to retry)
|
||||
tap opens S01 for that city; refresh icon only on ≥ 4×2
|
||||
|
||||
NowWidget 2×1: icon + temp | 2×2: + city, condition, H/T
|
||||
HourlyWidget 4×2: now block + 6 hours (icon, temp, time)
|
||||
DailyWidget 4×2: now + 5 days | 4×3: + range bars
|
||||
FullWidget 4×4: now, 5 hours, 5 days, alert line if active
|
||||
GlanceWidget 4×1: "Fr., 17. Okt. · ☀ 14°" one line, alert replaces date (two lines: event, agency)
|
||||
|
||||
type display-widget 44sp for temps, label for times; size by SizeMode.Responsive breakpoints
|
||||
a11y contentDescription on root summarising the widget
|
||||
config S12-config activity: location picker, style, live preview, "Fertig"
|
||||
```
|
||||
|
||||
## Screens → Komponenten (Status)
|
||||
|
||||
| Screen | Komponenten |
|
||||
| --- | --- |
|
||||
| S01 | WeatherScene, TopAppBar, HeroBlock, AlertBanner, BriefCard, HourlyStrip, DailyList, MetricCard×n, PagerIndicator |
|
||||
| S02 | TopAppBar, CityRow, SearchField |
|
||||
| S03 | SearchField, ResultList |
|
||||
| S04/S05 | HourlyStrip, chart, MetricCard expanded |
|
||||
| S06 | RadarMap |
|
||||
| S08/S09 | Settings rows |
|
||||
| S10 | MetricCard (dragging/placeholder) |
|
||||
| S11 | AlertBanner expanded, text |
|
||||
| S12 | Widgets |
|
||||
|
||||
## Abgleich mit Screenshots (ref-01..04), überschreibt Abweichungen oben
|
||||
|
||||
Gemessen an `replica/screens/ref-*.png`. Das Layout-Muster übernehmen wir, die Grafik zeichnen wir selbst.
|
||||
|
||||
- **HeroBlock:** zentrierte Spalte. Oben die Wetterlage (22sp), darunter die Temperatur sehr groß und **fett** (112sp, 700, nicht dünn) mit dem Wettericon direkt rechts daneben, etwa 40 % der Ziffernhöhe, oben ausgerichtet. Dann „Gefühlt 19°“ und „Höchstwert 22° · Tiefstwert 14°“. Am Tag ist der Text dunkel auf hellem Himmel, nachts weiß. Es gibt keinen Scrim, die hellen Szenen sind entsprechend hell gewählt.
|
||||
- **Szene:** ruhiger, weicher Verlauf mit großen, unscharfen, langsam treibenden Formen (Wolkenschemen, Hügel). Keine fotorealistische Darstellung. Bei Niederschlag fallen dezente Partikel.
|
||||
- **Karten-Container:** deckend (`card` bzw. `dark-card`, warm getöntes Neutral), Radius 28, Padding 20. Die Kopfzeile besteht aus einem Outline-Icon und dem Titel (card-title). Zwischen den Karten 12 dp Abstand, nach außen 16 dp Rand.
|
||||
- **HourlyStrip:** Die Spalten zeigen Temperatur, darunter Icon, darunter Niederschlag in % (`precip-text`, klein), darunter die Uhrzeit. Die aktuelle Stunde trägt einen **Cookie-Chip** (`now-chip`) hinter der Temperatur, das Label heißt „Jetzt“.
|
||||
- **DailyList:** **horizontal** scrollende Reihe vertikaler **Kapseln** (`pill`, Radius 24, ca. 56×190 dp). In jeder Kapsel stehen Höchstwert, Tiefstwert (muted), Icon, Niederschlag in % und Tag plus Datum. Ein Tipp auf die Kapsel öffnet S04.
|
||||
- **DayDetail (S04, bestätigt):** oben eine Kapselreihe, die gewählte Kapsel ist hervorgehoben (`pill-selected`). Darunter Datum, Ort, „24°/14°“ (day-range 56sp) mit Icon, Wetterlage, die stündliche Vorhersage für diesen Tag und darunter dieselben Detailkarten.
|
||||
- **Detailkarten:** Raster mit 2 Spalten aus quadratischen Kacheln, **Formen statt einheitlicher Karten**:
|
||||
- Wind: Kreis, darin ein abgerundetes Dreieck als Blob (`fill-*`), gedreht in die Windrichtung, mittig Wert und Einheit, darunter „Aus NW“
|
||||
- UV: Cookie mit 12 Wellen, Wert und Stufe, Punkte auf einem unteren Bogen, der aktive Punkt farbig
|
||||
- Sichtweite: Kreis mit gewellter Innenfläche (`fill-tertiary`), Wert und Einheit
|
||||
- Luftfeuchte: Rechteck, Füllstand mit gewellter Oberkante, Höhe = %, unten ein runder Chip mit dem Taupunkt
|
||||
- Luftdruck: Kreis mit 270°-Bogenanzeige, Wert groß, Einheit darunter
|
||||
- Sonne: Rechteck mit gefülltem Sinus-Hügel über der Horizontlinie, der Nachtteil dunkler, Sonnenpunkt auf der Kurve, Aufgang und Untergang als Zeiten
|
||||
- Luftqualität: Rechteck mit Verlaufsbalken und Knopf, Wert, Einstufung („Gute Luftqualität“); ohne Daten wird „– –“ gezeigt
|
||||
- **NowcastCard (neu):** „Regen ab 09:15“ als Titel, Untertitel „Leichter Regen von 09:15 bis 10:45“. Ein Punkt-Balkendiagramm zeigt die nächsten 2–3 Stunden in 15-Minuten-Schritten, die Y-Achse in drei Stufen (leicht, mäßig, stark). Ein Marker-Chip am Beginn, die X-Achse mit Uhrzeiten. Die Karte erscheint nur, wenn in den nächsten 3 Stunden Niederschlag kommt. Daten: Open-Meteo `minutely_15`.
|
||||
- **BriefCard:** wie eine normale Karte mit Icon, Titel „Kurz gesagt“ und einem Chevron zum Auf- und Zuklappen. Darunter 2–3 Sätze Text in body.
|
||||
- **Vorschau eines Orts (S15, neu):** Ein gesuchter Ort öffnet die volle Ansicht mit einem Button „Speichern“ oben rechts (gefüllter Pill-Button, Tonal).
|
||||
- **Abweichungen, die bewusst eigen bleiben:** eigene Icons und Szenen, ein eigener Akzent erst in `/replica-brand`, eigene Texte. Die Kartenformen sind M3-Expressive-Standardformen (`MaterialShapes`: Cookie, Circle, Triangle, Wavy), damit ist nichts vom Original nachgezeichnet.
|
||||
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env python3
|
||||
"""tokens.json -> Tokens.kt (Compose). Re-run after every token change."""
|
||||
import json, re, sys, pathlib
|
||||
src = pathlib.Path(sys.argv[1] if len(sys.argv) > 1 else "tokens.json")
|
||||
out = pathlib.Path(sys.argv[2] if len(sys.argv) > 2 else src.with_name("Tokens.kt"))
|
||||
t = json.loads(src.read_text())
|
||||
camel = lambda s: re.sub(r"-(\w)", lambda m: m.group(1).upper(), s)
|
||||
L = ["// Generated from tokens.json by gen_theme.py. Do not edit by hand.",
|
||||
"package app.wetter.ui.theme", "",
|
||||
"import androidx.compose.ui.graphics.Color",
|
||||
"import androidx.compose.ui.text.TextStyle",
|
||||
"import androidx.compose.ui.text.font.FontWeight",
|
||||
"import androidx.compose.ui.unit.dp",
|
||||
"import androidx.compose.ui.unit.sp", "",
|
||||
"object WColor {"]
|
||||
L += [f" val {camel(k)} = Color(0xFF{v.lstrip('#').upper()})" for k, v in t["color"].items()]
|
||||
L += ["}", "", "object WType {"]
|
||||
for k, v in t["type"].items():
|
||||
L.append(f" val {camel(k)} = TextStyle(fontSize = {v['size']}.sp, lineHeight = {v['line']}.sp, fontWeight = FontWeight.W{v['weight']})")
|
||||
L += ["}", "", "object WSpace {"]
|
||||
L += [f" val s{v} = {v}.dp" for v in t["space"]]
|
||||
L += ["}", "", "object WRadius {"]
|
||||
L += [f" val {camel(k)} = {v}.dp" for k, v in t["radius"].items()]
|
||||
L += ["}", ""]
|
||||
out.write_text("\n".join(L))
|
||||
print("wrote", out)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 108 108" width="432" height="432">
|
||||
<defs><linearGradient id="g" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#4E9BE6"/><stop offset="1" stop-color="#BFE3F7"/></linearGradient>
|
||||
<clipPath id="c"><circle cx="54" cy="54" r="54"/></clipPath></defs>
|
||||
<g clip-path="url(#c)"><rect width="108" height="108" fill="url(#g)"/>
|
||||
<path d="M0.00,108.00 L0.00,77.13 L2.00,76.77 L4.00,76.47 L6.00,76.25 L8.00,76.09 L10.00,76.01 L12.00,76.01 L14.00,76.09 L16.00,76.24 L18.00,76.46 L20.00,76.76 L22.00,77.11 L24.00,77.53 L26.00,77.99 L28.00,78.49 L30.00,79.02 L32.00,79.57 L34.00,80.13 L36.00,80.68 L38.00,81.23 L40.00,81.74 L42.00,82.23 L44.00,82.67 L46.00,83.06 L48.00,83.39 L50.00,83.65 L52.00,83.84 L54.00,83.96 L56.00,84.00 L58.00,83.96 L60.00,83.85 L62.00,83.66 L64.00,83.40 L66.00,83.07 L68.00,82.68 L70.00,82.25 L72.00,81.76 L74.00,81.25 L76.00,80.71 L78.00,80.15 L80.00,79.59 L82.00,79.04 L84.00,78.51 L86.00,78.01 L88.00,77.55 L90.00,77.13 L92.00,76.77 L94.00,76.47 L96.00,76.25 L98.00,76.09 L100.00,76.01 L102.00,76.01 L104.00,76.09 L106.00,76.24 L108.00,76.46 L108.00,108.00 Z" fill="#8EC2EC"/><path d="M0.00,108.00 L0.00,85.97 L2.00,86.20 L4.00,86.44 L6.00,86.69 L8.00,86.96 L10.00,87.23 L12.00,87.52 L14.00,87.81 L16.00,88.10 L18.00,88.38 L20.00,88.67 L22.00,88.95 L24.00,89.22 L26.00,89.48 L28.00,89.72 L30.00,89.95 L32.00,90.16 L34.00,90.35 L36.00,90.52 L38.00,90.67 L40.00,90.79 L42.00,90.88 L44.00,90.95 L46.00,90.99 L48.00,91.00 L50.00,90.98 L52.00,90.94 L54.00,90.87 L56.00,90.77 L58.00,90.65 L60.00,90.50 L62.00,90.32 L64.00,90.13 L66.00,89.92 L68.00,89.69 L70.00,89.44 L72.00,89.18 L74.00,88.91 L76.00,88.63 L78.00,88.34 L80.00,88.05 L82.00,87.76 L84.00,87.47 L86.00,87.19 L88.00,86.92 L90.00,86.65 L92.00,86.40 L94.00,86.16 L96.00,85.94 L98.00,85.74 L100.00,85.56 L102.00,85.40 L104.00,85.27 L106.00,85.16 L108.00,85.08 L108.00,108.00 Z" fill="#A9D3F2"/>
|
||||
<path d="M78.41,44.00 L78.32,44.76 L78.08,45.49 L77.70,46.20 L77.25,46.87 L76.77,47.50 L76.32,48.10 L75.94,48.71 L75.65,49.33 L75.47,49.99 L75.37,50.70 L75.33,51.46 L75.29,52.25 L75.21,53.05 L75.04,53.83 L74.76,54.55 L74.33,55.19 L73.78,55.71 L73.12,56.12 L72.38,56.42 L71.61,56.64 L70.83,56.81 L70.10,56.99 L69.41,57.21 L68.80,57.50 L68.23,57.89 L67.70,58.37 L67.18,58.92 L66.64,59.50 L66.07,60.07 L65.44,60.56 L64.75,60.93 L64.02,61.14 L63.26,61.19 L62.49,61.08 L61.73,60.83 L61.00,60.50 L60.30,60.14 L59.62,59.80 L58.96,59.53 L58.29,59.36 L57.61,59.29 L56.90,59.32 L56.14,59.41 L55.36,59.50 L54.55,59.56 L53.76,59.54 L52.99,59.38 L52.30,59.08 L51.68,58.62 L51.17,58.04 L50.74,57.37 L50.39,56.64 L50.09,55.91 L49.79,55.21 L49.45,54.58 L49.06,54.02 L48.57,53.53 L48.01,53.10 L47.38,52.68 L46.71,52.25 L46.06,51.78 L45.46,51.24 L44.98,50.64 L44.64,49.95 L44.46,49.21 L44.44,48.44 L44.55,47.65 L44.75,46.87 L44.99,46.11 L45.20,45.38 L45.35,44.68 L45.41,44.00 L45.35,43.32 L45.20,42.62 L44.99,41.89 L44.75,41.13 L44.55,40.35 L44.44,39.56 L44.46,38.79 L44.64,38.05 L44.98,37.36 L45.46,36.76 L46.06,36.22 L46.71,35.75 L47.38,35.32 L48.01,34.90 L48.57,34.47 L49.06,33.98 L49.45,33.42 L49.79,32.79 L50.09,32.09 L50.39,31.36 L50.74,30.63 L51.17,29.96 L51.68,29.38 L52.30,28.92 L52.99,28.62 L53.76,28.46 L54.55,28.44 L55.36,28.50 L56.14,28.59 L56.90,28.68 L57.61,28.71 L58.29,28.64 L58.96,28.47 L59.62,28.20 L60.30,27.86 L61.00,27.50 L61.73,27.17 L62.49,26.92 L63.26,26.81 L64.02,26.86 L64.75,27.07 L65.44,27.44 L66.07,27.93 L66.64,28.50 L67.18,29.08 L67.70,29.63 L68.23,30.11 L68.80,30.50 L69.41,30.79 L70.10,31.01 L70.83,31.19 L71.61,31.36 L72.38,31.58 L73.12,31.88 L73.78,32.29 L74.33,32.81 L74.76,33.45 L75.04,34.17 L75.21,34.95 L75.29,35.75 L75.33,36.54 L75.37,37.30 L75.47,38.01 L75.65,38.67 L75.94,39.29 L76.32,39.90 L76.77,40.50 L77.25,41.13 L77.70,41.80 L78.08,42.51 L78.32,43.24 Z" fill="#FFB02E"/><circle cx="61" cy="44" r="10" fill="#FF9A1F" opacity=".35"/>
|
||||
<path d="M38,74 h30 a9,9 0 0 0 0,-18 a12,12 0 0 0 -22.5,-3.5 a10,10 0 0 0 -7.5,21.5 z" fill="#D5DEE8" transform="translate(0,2.5)"/><path d="M38,74 h30 a9,9 0 0 0 0,-18 a12,12 0 0 0 -22.5,-3.5 a10,10 0 0 0 -7.5,21.5 z" fill="#FFFFFF"/></g></svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1,764 @@
|
||||
{
|
||||
"_about": "Wetter-Klon tokens. Roles measured by eye from replica/screens/ref-01..04 (Pixel Weather, 2024-25). Values are own approximations, no brand colour copied. Names stay, values change in /replica-brand. Check: python3 ~/.claude/skills/replica-design/contrast.py replica/wetter/design/tokens.json",
|
||||
"color": {
|
||||
"bg": "#f8f9ff",
|
||||
"surface": "#ffffff",
|
||||
"surface-container": "#eceef4",
|
||||
"surface-container-high": "#e1e3ea",
|
||||
"border": "#c3c6cf",
|
||||
"border-input": "#73777f",
|
||||
"text": "#191c20",
|
||||
"text-muted": "#43474e",
|
||||
"accent": "#2a5ea8",
|
||||
"on-accent": "#ffffff",
|
||||
"accent-container": "#d6e3ff",
|
||||
"on-accent-container": "#001b3e",
|
||||
"danger": "#ba1a1a",
|
||||
"warning": "#8a5100",
|
||||
"success": "#2e6b30",
|
||||
"dark-bg": "#111318",
|
||||
"dark-surface": "#1d2024",
|
||||
"dark-surface-container": "#272a2f",
|
||||
"dark-border": "#43474e",
|
||||
"dark-border-input": "#8d9199",
|
||||
"dark-text": "#e2e2e9",
|
||||
"dark-text-muted": "#c3c6cf",
|
||||
"dark-accent": "#a9c7ff",
|
||||
"dark-on-accent": "#003063",
|
||||
"dark-danger": "#ffb4ab",
|
||||
"dark-warning": "#ffb870",
|
||||
"scene-clear-day-top": "#7cc4ef",
|
||||
"scene-clear-day-bottom": "#bfe3f7",
|
||||
"scene-clear-night-top": "#3d4f9a",
|
||||
"scene-clear-night-bottom": "#5b4f8f",
|
||||
"scene-cloudy-day-top": "#c9cdd2",
|
||||
"scene-cloudy-day-bottom": "#dfe2e5",
|
||||
"scene-rain-top": "#2e3d4f",
|
||||
"scene-rain-bottom": "#5a6b7d",
|
||||
"scene-snow-top": "#7c8ea3",
|
||||
"scene-snow-bottom": "#d4dde6",
|
||||
"scene-storm-top": "#1f1a33",
|
||||
"scene-storm-bottom": "#4a3f63",
|
||||
"scene-fog-top": "#8a929b",
|
||||
"scene-fog-bottom": "#c4c9ce",
|
||||
"on-scene": "#ffffff",
|
||||
"on-scene-muted": "#e8edf3",
|
||||
"on-scene-dark": "#14181d",
|
||||
"scene-card": "#2a3644",
|
||||
"scene-card-light": "#f2f5f8",
|
||||
"alert-minor": "#fff2b8",
|
||||
"on-alert-minor": "#3d2f00",
|
||||
"alert-severe": "#ffdbc8",
|
||||
"on-alert-severe": "#4a1a00",
|
||||
"alert-extreme": "#93000a",
|
||||
"on-alert-extreme": "#ffffff",
|
||||
"aqi-good": "#2e7d32",
|
||||
"aqi-moderate": "#a06a00",
|
||||
"aqi-bad": "#c62828",
|
||||
"uv-high": "#b34700",
|
||||
"card": "#fdf8f3",
|
||||
"on-card": "#1f1b17",
|
||||
"on-card-muted": "#5a524b",
|
||||
"dark-card": "#1f1714",
|
||||
"dark-on-card": "#ede0db",
|
||||
"dark-on-card-muted": "#c4b6b0",
|
||||
"fill-primary": "#d3e3fd",
|
||||
"on-fill-primary": "#0b2a55",
|
||||
"fill-secondary": "#e9ddff",
|
||||
"on-fill-secondary": "#2a1a4d",
|
||||
"fill-tertiary": "#c8e6c2",
|
||||
"on-fill-tertiary": "#0f3a12",
|
||||
"dark-fill-primary": "#a8c7fa",
|
||||
"dark-on-fill-primary": "#062e6f",
|
||||
"dark-fill-tertiary": "#2d5a27",
|
||||
"dark-on-fill-tertiary": "#b7f0ac",
|
||||
"dark-fill-olive": "#3e4a1e",
|
||||
"pill": "#f1ebe5",
|
||||
"dark-pill": "#2b221e",
|
||||
"pill-selected": "#a8c7fa",
|
||||
"on-pill-selected": "#0b2a55",
|
||||
"now-chip": "#4e4038",
|
||||
"on-now-chip": "#ffffff",
|
||||
"precip-text": "#2a5ea8",
|
||||
"dark-precip-text": "#a8c7fa",
|
||||
"nowcast-dot": "#2e7d32",
|
||||
"icon-sun": "#ffb02e",
|
||||
"icon-sun-core": "#ff9a1f",
|
||||
"icon-moon": "#a9c4ff",
|
||||
"icon-cloud": "#f4f6f9",
|
||||
"icon-cloud-shade": "#cfd6df",
|
||||
"icon-cloud-dark": "#9aa5b3",
|
||||
"icon-drop": "#3d7bf0",
|
||||
"icon-snow": "#ffffff",
|
||||
"icon-snow-outline": "#9fb3cc",
|
||||
"icon-bolt": "#ffc93c",
|
||||
"icon-fog": "#c3cad3",
|
||||
"scene-glow-sun": "#fff3c4",
|
||||
"scene-cloud": "#ffffff",
|
||||
"scene-hill": "#9fc5e6",
|
||||
"dark-scene-hill": "#2f3f78",
|
||||
"range-cold": "#6fa8ff",
|
||||
"range-warm": "#ffb02e",
|
||||
"aqi-bar-1": "#4fd1c5",
|
||||
"aqi-bar-2": "#9ccc65",
|
||||
"aqi-bar-3": "#ffd54f",
|
||||
"aqi-bar-4": "#ff8a65",
|
||||
"aqi-bar-5": "#e53935",
|
||||
"aqi-bar-6": "#8e24aa",
|
||||
"uv-dot-low": "#5ccf8a",
|
||||
"uv-dot-off": "#6b5a52",
|
||||
"widget-ocean-light-bg": "#cce4f0",
|
||||
"widget-ocean-light-on": "#0f2733",
|
||||
"widget-ocean-light-accent": "#20546f",
|
||||
"widget-ocean-light-panel": "#f9fafb",
|
||||
"widget-ocean-light-onpanel": "#192124",
|
||||
"widget-ocean-light-badge": "#a8d1e6",
|
||||
"widget-ocean-light-onbadge": "#0f2733",
|
||||
"widget-ocean-dark-bg": "#194257",
|
||||
"widget-ocean-dark-on": "#dbecf5",
|
||||
"widget-ocean-dark-accent": "#b0d5e8",
|
||||
"widget-ocean-dark-panel": "#0e1315",
|
||||
"widget-ocean-dark-onpanel": "#e7ecef",
|
||||
"widget-ocean-dark-badge": "#1f516b",
|
||||
"widget-ocean-dark-onbadge": "#dfeef6",
|
||||
"widget-forest-light-bg": "#d2e9da",
|
||||
"widget-forest-light-on": "#162d1d",
|
||||
"widget-forest-light-accent": "#2e603f",
|
||||
"widget-forest-light-panel": "#f9fbfa",
|
||||
"widget-forest-light-onpanel": "#1b221d",
|
||||
"widget-forest-light-badge": "#b3dbc0",
|
||||
"widget-forest-light-onbadge": "#162d1d",
|
||||
"widget-forest-dark-bg": "#244c32",
|
||||
"widget-forest-dark-on": "#e0f0e5",
|
||||
"widget-forest-dark-accent": "#badec6",
|
||||
"widget-forest-dark-panel": "#101411",
|
||||
"widget-forest-dark-onpanel": "#e8edea",
|
||||
"widget-forest-dark-badge": "#2d5d3d",
|
||||
"widget-forest-dark-onbadge": "#e3f2e8",
|
||||
"widget-sun-light-bg": "#f7e4c5",
|
||||
"widget-sun-light-on": "#3a2808",
|
||||
"widget-sun-light-accent": "#7d5612",
|
||||
"widget-sun-light-panel": "#fbfaf9",
|
||||
"widget-sun-light-onpanel": "#272117",
|
||||
"widget-sun-light-badge": "#f1d29d",
|
||||
"widget-sun-light-onbadge": "#3a2808",
|
||||
"widget-sun-dark-bg": "#62430e",
|
||||
"widget-sun-dark-on": "#f9edd7",
|
||||
"widget-sun-dark-accent": "#f2d6a6",
|
||||
"widget-sun-dark-panel": "#17130d",
|
||||
"widget-sun-dark-onpanel": "#f0ece5",
|
||||
"widget-sun-dark-badge": "#785311",
|
||||
"widget-sun-dark-onbadge": "#faefdb",
|
||||
"widget-lavender-light-bg": "#dbcfed",
|
||||
"widget-lavender-light-on": "#1f1230",
|
||||
"widget-lavender-light-accent": "#422768",
|
||||
"widget-lavender-light-panel": "#faf9fb",
|
||||
"widget-lavender-light-onpanel": "#1e1a23",
|
||||
"widget-lavender-light-badge": "#c3aee0",
|
||||
"widget-lavender-light-onbadge": "#1f1230",
|
||||
"widget-lavender-dark-bg": "#341f51",
|
||||
"widget-lavender-dark-on": "#e6def2",
|
||||
"widget-lavender-dark-accent": "#c8b5e3",
|
||||
"widget-lavender-dark-panel": "#110f15",
|
||||
"widget-lavender-dark-onpanel": "#eae7ee",
|
||||
"widget-lavender-dark-badge": "#402664",
|
||||
"widget-lavender-dark-onbadge": "#e9e1f4",
|
||||
"widget-graphite-light-bg": "#dbdde1",
|
||||
"widget-graphite-light-on": "#1e2124",
|
||||
"widget-graphite-light-accent": "#42464d",
|
||||
"widget-graphite-light-panel": "#fafafa",
|
||||
"widget-graphite-light-onpanel": "#1e1e1f",
|
||||
"widget-graphite-light-badge": "#c2c6cb",
|
||||
"widget-graphite-light-onbadge": "#1e2124",
|
||||
"widget-graphite-dark-bg": "#34373d",
|
||||
"widget-graphite-dark-on": "#e6e8ea",
|
||||
"widget-graphite-dark-accent": "#c8cbd0",
|
||||
"widget-graphite-dark-panel": "#111212",
|
||||
"widget-graphite-dark-onpanel": "#eaebeb",
|
||||
"widget-graphite-dark-badge": "#3f444a",
|
||||
"widget-graphite-dark-onbadge": "#e9eaec",
|
||||
"dark-accent-container": "#244a80",
|
||||
"dark-on-accent-container": "#d6e3ff"
|
||||
},
|
||||
"pairs": [
|
||||
[
|
||||
"text",
|
||||
"bg"
|
||||
],
|
||||
[
|
||||
"text",
|
||||
"surface"
|
||||
],
|
||||
[
|
||||
"text",
|
||||
"surface-container"
|
||||
],
|
||||
[
|
||||
"text",
|
||||
"surface-container-high"
|
||||
],
|
||||
[
|
||||
"text-muted",
|
||||
"bg"
|
||||
],
|
||||
[
|
||||
"text-muted",
|
||||
"surface"
|
||||
],
|
||||
[
|
||||
"text-muted",
|
||||
"surface-container"
|
||||
],
|
||||
[
|
||||
"on-accent",
|
||||
"accent"
|
||||
],
|
||||
[
|
||||
"on-accent-container",
|
||||
"accent-container"
|
||||
],
|
||||
[
|
||||
"accent",
|
||||
"bg"
|
||||
],
|
||||
[
|
||||
"accent",
|
||||
"surface"
|
||||
],
|
||||
[
|
||||
"danger",
|
||||
"surface"
|
||||
],
|
||||
[
|
||||
"warning",
|
||||
"surface"
|
||||
],
|
||||
[
|
||||
"success",
|
||||
"surface"
|
||||
],
|
||||
[
|
||||
"border-input",
|
||||
"surface",
|
||||
"ui"
|
||||
],
|
||||
[
|
||||
"accent",
|
||||
"surface",
|
||||
"ui"
|
||||
],
|
||||
[
|
||||
"dark-text",
|
||||
"dark-bg"
|
||||
],
|
||||
[
|
||||
"dark-text",
|
||||
"dark-surface"
|
||||
],
|
||||
[
|
||||
"dark-text",
|
||||
"dark-surface-container"
|
||||
],
|
||||
[
|
||||
"dark-text-muted",
|
||||
"dark-bg"
|
||||
],
|
||||
[
|
||||
"dark-text-muted",
|
||||
"dark-surface"
|
||||
],
|
||||
[
|
||||
"dark-text-muted",
|
||||
"dark-surface-container"
|
||||
],
|
||||
[
|
||||
"dark-on-accent",
|
||||
"dark-accent"
|
||||
],
|
||||
[
|
||||
"dark-accent",
|
||||
"dark-surface"
|
||||
],
|
||||
[
|
||||
"dark-danger",
|
||||
"dark-surface"
|
||||
],
|
||||
[
|
||||
"dark-warning",
|
||||
"dark-surface"
|
||||
],
|
||||
[
|
||||
"dark-border-input",
|
||||
"dark-surface",
|
||||
"ui"
|
||||
],
|
||||
[
|
||||
"on-scene",
|
||||
"scene-clear-night-top"
|
||||
],
|
||||
[
|
||||
"on-scene",
|
||||
"scene-clear-night-bottom"
|
||||
],
|
||||
[
|
||||
"on-scene",
|
||||
"scene-rain-top"
|
||||
],
|
||||
[
|
||||
"on-scene",
|
||||
"scene-rain-bottom"
|
||||
],
|
||||
[
|
||||
"on-scene",
|
||||
"scene-storm-top"
|
||||
],
|
||||
[
|
||||
"on-scene",
|
||||
"scene-storm-bottom"
|
||||
],
|
||||
[
|
||||
"on-scene-dark",
|
||||
"scene-clear-day-bottom"
|
||||
],
|
||||
[
|
||||
"on-scene-dark",
|
||||
"scene-cloudy-day-bottom"
|
||||
],
|
||||
[
|
||||
"on-scene-dark",
|
||||
"scene-snow-top"
|
||||
],
|
||||
[
|
||||
"on-scene-dark",
|
||||
"scene-snow-bottom"
|
||||
],
|
||||
[
|
||||
"on-scene-dark",
|
||||
"scene-fog-top"
|
||||
],
|
||||
[
|
||||
"on-scene-dark",
|
||||
"scene-fog-bottom"
|
||||
],
|
||||
[
|
||||
"on-scene",
|
||||
"scene-card"
|
||||
],
|
||||
[
|
||||
"on-scene-muted",
|
||||
"scene-card"
|
||||
],
|
||||
[
|
||||
"on-scene-dark",
|
||||
"scene-card-light"
|
||||
],
|
||||
[
|
||||
"on-alert-minor",
|
||||
"alert-minor"
|
||||
],
|
||||
[
|
||||
"on-alert-severe",
|
||||
"alert-severe"
|
||||
],
|
||||
[
|
||||
"on-alert-extreme",
|
||||
"alert-extreme"
|
||||
],
|
||||
[
|
||||
"aqi-good",
|
||||
"surface",
|
||||
"ui"
|
||||
],
|
||||
[
|
||||
"aqi-moderate",
|
||||
"surface",
|
||||
"ui"
|
||||
],
|
||||
[
|
||||
"aqi-bad",
|
||||
"surface",
|
||||
"ui"
|
||||
],
|
||||
[
|
||||
"uv-high",
|
||||
"surface",
|
||||
"ui"
|
||||
],
|
||||
[
|
||||
"on-scene-dark",
|
||||
"scene-clear-day-top"
|
||||
],
|
||||
[
|
||||
"on-scene-dark",
|
||||
"scene-cloudy-day-top"
|
||||
],
|
||||
[
|
||||
"on-card",
|
||||
"card"
|
||||
],
|
||||
[
|
||||
"on-card-muted",
|
||||
"card"
|
||||
],
|
||||
[
|
||||
"dark-on-card",
|
||||
"dark-card"
|
||||
],
|
||||
[
|
||||
"dark-on-card-muted",
|
||||
"dark-card"
|
||||
],
|
||||
[
|
||||
"on-fill-primary",
|
||||
"fill-primary"
|
||||
],
|
||||
[
|
||||
"on-fill-secondary",
|
||||
"fill-secondary"
|
||||
],
|
||||
[
|
||||
"on-fill-tertiary",
|
||||
"fill-tertiary"
|
||||
],
|
||||
[
|
||||
"dark-on-fill-primary",
|
||||
"dark-fill-primary"
|
||||
],
|
||||
[
|
||||
"dark-on-fill-tertiary",
|
||||
"dark-fill-tertiary"
|
||||
],
|
||||
[
|
||||
"dark-on-card",
|
||||
"dark-fill-olive"
|
||||
],
|
||||
[
|
||||
"on-card",
|
||||
"pill"
|
||||
],
|
||||
[
|
||||
"dark-on-card",
|
||||
"dark-pill"
|
||||
],
|
||||
[
|
||||
"on-pill-selected",
|
||||
"pill-selected"
|
||||
],
|
||||
[
|
||||
"on-now-chip",
|
||||
"now-chip"
|
||||
],
|
||||
[
|
||||
"precip-text",
|
||||
"card"
|
||||
],
|
||||
[
|
||||
"precip-text",
|
||||
"pill"
|
||||
],
|
||||
[
|
||||
"dark-precip-text",
|
||||
"dark-card"
|
||||
],
|
||||
[
|
||||
"dark-precip-text",
|
||||
"dark-pill"
|
||||
],
|
||||
[
|
||||
"nowcast-dot",
|
||||
"card",
|
||||
"ui"
|
||||
],
|
||||
[
|
||||
"widget-ocean-light-on",
|
||||
"widget-ocean-light-bg"
|
||||
],
|
||||
[
|
||||
"widget-ocean-light-accent",
|
||||
"widget-ocean-light-bg"
|
||||
],
|
||||
[
|
||||
"widget-ocean-light-onpanel",
|
||||
"widget-ocean-light-panel"
|
||||
],
|
||||
[
|
||||
"widget-ocean-light-onbadge",
|
||||
"widget-ocean-light-badge"
|
||||
],
|
||||
[
|
||||
"widget-ocean-dark-on",
|
||||
"widget-ocean-dark-bg"
|
||||
],
|
||||
[
|
||||
"widget-ocean-dark-accent",
|
||||
"widget-ocean-dark-bg"
|
||||
],
|
||||
[
|
||||
"widget-ocean-dark-onpanel",
|
||||
"widget-ocean-dark-panel"
|
||||
],
|
||||
[
|
||||
"widget-ocean-dark-onbadge",
|
||||
"widget-ocean-dark-badge"
|
||||
],
|
||||
[
|
||||
"widget-forest-light-on",
|
||||
"widget-forest-light-bg"
|
||||
],
|
||||
[
|
||||
"widget-forest-light-accent",
|
||||
"widget-forest-light-bg"
|
||||
],
|
||||
[
|
||||
"widget-forest-light-onpanel",
|
||||
"widget-forest-light-panel"
|
||||
],
|
||||
[
|
||||
"widget-forest-light-onbadge",
|
||||
"widget-forest-light-badge"
|
||||
],
|
||||
[
|
||||
"widget-forest-dark-on",
|
||||
"widget-forest-dark-bg"
|
||||
],
|
||||
[
|
||||
"widget-forest-dark-accent",
|
||||
"widget-forest-dark-bg"
|
||||
],
|
||||
[
|
||||
"widget-forest-dark-onpanel",
|
||||
"widget-forest-dark-panel"
|
||||
],
|
||||
[
|
||||
"widget-forest-dark-onbadge",
|
||||
"widget-forest-dark-badge"
|
||||
],
|
||||
[
|
||||
"widget-sun-light-on",
|
||||
"widget-sun-light-bg"
|
||||
],
|
||||
[
|
||||
"widget-sun-light-accent",
|
||||
"widget-sun-light-bg"
|
||||
],
|
||||
[
|
||||
"widget-sun-light-onpanel",
|
||||
"widget-sun-light-panel"
|
||||
],
|
||||
[
|
||||
"widget-sun-light-onbadge",
|
||||
"widget-sun-light-badge"
|
||||
],
|
||||
[
|
||||
"widget-sun-dark-on",
|
||||
"widget-sun-dark-bg"
|
||||
],
|
||||
[
|
||||
"widget-sun-dark-accent",
|
||||
"widget-sun-dark-bg"
|
||||
],
|
||||
[
|
||||
"widget-sun-dark-onpanel",
|
||||
"widget-sun-dark-panel"
|
||||
],
|
||||
[
|
||||
"widget-sun-dark-onbadge",
|
||||
"widget-sun-dark-badge"
|
||||
],
|
||||
[
|
||||
"widget-lavender-light-on",
|
||||
"widget-lavender-light-bg"
|
||||
],
|
||||
[
|
||||
"widget-lavender-light-accent",
|
||||
"widget-lavender-light-bg"
|
||||
],
|
||||
[
|
||||
"widget-lavender-light-onpanel",
|
||||
"widget-lavender-light-panel"
|
||||
],
|
||||
[
|
||||
"widget-lavender-light-onbadge",
|
||||
"widget-lavender-light-badge"
|
||||
],
|
||||
[
|
||||
"widget-lavender-dark-on",
|
||||
"widget-lavender-dark-bg"
|
||||
],
|
||||
[
|
||||
"widget-lavender-dark-accent",
|
||||
"widget-lavender-dark-bg"
|
||||
],
|
||||
[
|
||||
"widget-lavender-dark-onpanel",
|
||||
"widget-lavender-dark-panel"
|
||||
],
|
||||
[
|
||||
"widget-lavender-dark-onbadge",
|
||||
"widget-lavender-dark-badge"
|
||||
],
|
||||
[
|
||||
"widget-graphite-light-on",
|
||||
"widget-graphite-light-bg"
|
||||
],
|
||||
[
|
||||
"widget-graphite-light-accent",
|
||||
"widget-graphite-light-bg"
|
||||
],
|
||||
[
|
||||
"widget-graphite-light-onpanel",
|
||||
"widget-graphite-light-panel"
|
||||
],
|
||||
[
|
||||
"widget-graphite-light-onbadge",
|
||||
"widget-graphite-light-badge"
|
||||
],
|
||||
[
|
||||
"widget-graphite-dark-on",
|
||||
"widget-graphite-dark-bg"
|
||||
],
|
||||
[
|
||||
"widget-graphite-dark-accent",
|
||||
"widget-graphite-dark-bg"
|
||||
],
|
||||
[
|
||||
"widget-graphite-dark-onpanel",
|
||||
"widget-graphite-dark-panel"
|
||||
],
|
||||
[
|
||||
"widget-graphite-dark-onbadge",
|
||||
"widget-graphite-dark-badge"
|
||||
],
|
||||
[
|
||||
"dark-on-accent-container",
|
||||
"dark-accent-container"
|
||||
]
|
||||
],
|
||||
"font": {
|
||||
"sans": "Roboto Flex (OFL), system default fallback",
|
||||
"display": "Roboto Flex, weight 700-800, tight tracking (-2%) for the hero temperature",
|
||||
"mono": "Roboto Mono"
|
||||
},
|
||||
"type": {
|
||||
"label-sm": {
|
||||
"size": 11,
|
||||
"line": 16,
|
||||
"weight": 500
|
||||
},
|
||||
"label": {
|
||||
"size": 12,
|
||||
"line": 16,
|
||||
"weight": 500
|
||||
},
|
||||
"body-sm": {
|
||||
"size": 14,
|
||||
"line": 20,
|
||||
"weight": 400
|
||||
},
|
||||
"body": {
|
||||
"size": 16,
|
||||
"line": 24,
|
||||
"weight": 400
|
||||
},
|
||||
"title": {
|
||||
"size": 18,
|
||||
"line": 24,
|
||||
"weight": 500
|
||||
},
|
||||
"headline": {
|
||||
"size": 24,
|
||||
"line": 32,
|
||||
"weight": 500
|
||||
},
|
||||
"metric": {
|
||||
"size": 32,
|
||||
"line": 40,
|
||||
"weight": 500
|
||||
},
|
||||
"display": {
|
||||
"size": 112,
|
||||
"line": 112,
|
||||
"weight": 700
|
||||
},
|
||||
"display-widget": {
|
||||
"size": 44,
|
||||
"line": 48,
|
||||
"weight": 400
|
||||
},
|
||||
"day-range": {
|
||||
"size": 56,
|
||||
"line": 60,
|
||||
"weight": 600
|
||||
},
|
||||
"hero-condition": {
|
||||
"size": 22,
|
||||
"line": 28,
|
||||
"weight": 400
|
||||
},
|
||||
"card-title": {
|
||||
"size": 16,
|
||||
"line": 24,
|
||||
"weight": 500
|
||||
}
|
||||
},
|
||||
"space": [
|
||||
0,
|
||||
2,
|
||||
4,
|
||||
8,
|
||||
12,
|
||||
16,
|
||||
20,
|
||||
24,
|
||||
32,
|
||||
48
|
||||
],
|
||||
"radius": {
|
||||
"xs": 8,
|
||||
"sm": 12,
|
||||
"md": 16,
|
||||
"lg": 24,
|
||||
"xl": 28,
|
||||
"widget": 24,
|
||||
"pill": 999,
|
||||
"card": 28,
|
||||
"day-pill": 24
|
||||
},
|
||||
"shape": {
|
||||
"wind": "circle container, inner rounded-triangle blob rotated to wind direction",
|
||||
"uv": "scalloped 12-wave cookie, dots on a lower arc show the UV band",
|
||||
"visibility": "circle container, inner wavy-edged circle fill",
|
||||
"humidity": "rounded rect, wave-topped fill whose height = humidity",
|
||||
"pressure": "circle container, 270° arc gauge",
|
||||
"sun": "rounded rect, filled sine hill above a horizon line, night part darker",
|
||||
"aqi": "rounded rect, gradient bar with knob",
|
||||
"now-chip": "8-point soft cookie behind current-hour temp",
|
||||
"day": "vertical capsule per day (10-day row scrolls horizontally)"
|
||||
},
|
||||
"elevation": {
|
||||
"card": 0,
|
||||
"card-drag": 6,
|
||||
"sheet": 1,
|
||||
"fab": 3
|
||||
},
|
||||
"motion": {
|
||||
"spring-fast": "dampingRatio 0.9, stiffness 1400",
|
||||
"spring-default": "dampingRatio 0.8, stiffness 380",
|
||||
"spring-bouncy": "dampingRatio 0.6, stiffness 300",
|
||||
"scene-crossfade": "600ms",
|
||||
"fast": "120ms",
|
||||
"base": "250ms"
|
||||
},
|
||||
"layout": {
|
||||
"gutter": 16,
|
||||
"card-gap": 12,
|
||||
"grid-compact": 2,
|
||||
"grid-expanded": 4,
|
||||
"two-pane-from": "600dp",
|
||||
"hero-height": "~45% of viewport",
|
||||
"app-bar": 64,
|
||||
"hero": "centered column: condition, temp + inline icon, feels like, high · low",
|
||||
"card-padding": 20,
|
||||
"detail-grid": "2 columns, circles left / rects right, equal squares"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user