wip: glass theme tokens + demo page + charcoal base port

This commit is contained in:
avi 2026-09-07 18:12:09 -05:00
commit 6628965e68
941 changed files with 1688 additions and 1160 deletions

View file

@ -8,9 +8,11 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.navigation.compose.rememberNavController
import com.rada.player.PlaybackManager
import com.rada.ui.navigation.NavGraph
import com.rada.ui.theme.GlassBackground
import com.rada.ui.theme.RadAmpTheme
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@ -27,11 +29,13 @@ class MainActivity : ComponentActivity() {
setContent {
RadAmpTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background,
) {
NavGraph(navController = rememberNavController())
GlassBackground {
Surface(
modifier = Modifier.fillMaxSize(),
color = Color.Transparent,
) {
NavGraph(navController = rememberNavController())
}
}
}
}

View file

@ -196,12 +196,12 @@ fun LibraryScreen(
}
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.background,
containerColor = Color.Transparent,
),
)
},
snackbarHost = { SnackbarHost(snackbarHostState) },
containerColor = MaterialTheme.colorScheme.background,
containerColor = Color.Transparent,
) { padding ->
Column(
modifier = Modifier
@ -213,7 +213,7 @@ fun LibraryScreen(
selectedTabIndex = selectedTabIndex,
modifier = Modifier.fillMaxWidth(),
edgePadding = 16.dp,
containerColor = MaterialTheme.colorScheme.background,
containerColor = Color.Transparent,
contentColor = PremiumPurple,
indicator = { tabPositions ->
if (selectedTabIndex < tabPositions.size) {

View file

@ -4,8 +4,12 @@ import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@ -15,7 +19,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MusicNote
import androidx.compose.material.icons.filled.Radio
import androidx.compose.material.icons.filled.Pause
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material3.Icon
@ -30,14 +34,15 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.rada.player.PlaybackManager
import com.rada.ui.theme.PremiumBorder
import com.rada.ui.theme.PremiumPurple
import com.rada.ui.theme.PremiumSurface
import com.rada.ui.theme.RadAmpGlass
import com.rada.ui.theme.PremiumText
import com.rada.ui.theme.PremiumTextSecondary
@ -59,7 +64,12 @@ fun NowPlayingBar(
Column(
modifier = Modifier
.fillMaxWidth()
.background(PremiumSurface)
.padding(horizontal = 12.dp)
.clip(RoundedCornerShape(RadAmpGlass.rCard))
// glass: textSafe 90% overlay + 6% white + highlight border
.background(RadAmpGlass.textSafe)
.background(RadAmpGlass.surface)
.border(1.dp, RadAmpGlass.border, RoundedCornerShape(RadAmpGlass.rCard))
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
@ -86,12 +96,21 @@ fun NowPlayingBar(
.padding(start = 12.dp, end = 4.dp, top = 10.dp, bottom = 10.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
Icons.Default.MusicNote,
contentDescription = null,
tint = PremiumPurple,
modifier = Modifier.size(22.dp),
)
// violet icon chip (glass hero element)
Box(
modifier = Modifier
.size(48.dp)
.clip(RoundedCornerShape(14.dp))
.background(PremiumPurple),
contentAlignment = Alignment.Center,
) {
Icon(
Icons.Default.Radio,
contentDescription = null,
tint = Color.White,
modifier = Modifier.size(24.dp),
)
}
Spacer(modifier = Modifier.width(12.dp))
Column(
modifier = Modifier

View file

@ -191,10 +191,11 @@ fun PlayerScreen(
}
Scaffold(
containerColor = Color.Transparent,
snackbarHost = { SnackbarHost(snackbarHostState) },
bottomBar = {
BottomAppBar(
containerColor = MaterialTheme.colorScheme.surface,
containerColor = PremiumSurface.copy(alpha = 0.94f),
contentColor = MaterialTheme.colorScheme.onSurface,
) {
NavigationBarItem(

View file

@ -52,6 +52,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
@ -151,12 +152,12 @@ fun PlaylistManagerScreen(
}
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.background,
containerColor = Color.Transparent,
),
)
},
snackbarHost = { SnackbarHost(snackbarHostState) },
containerColor = MaterialTheme.colorScheme.background,
containerColor = Color.Transparent,
) { padding ->
Column(
modifier = Modifier

View file

@ -46,6 +46,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
@ -130,11 +131,11 @@ fun StationBrowserScreen(
}
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.background,
containerColor = Color.Transparent,
),
)
},
containerColor = MaterialTheme.colorScheme.background,
containerColor = Color.Transparent,
) { padding ->
Column(
modifier = Modifier

View file

@ -117,11 +117,11 @@ fun StreamsScreen(
}
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.background,
containerColor = Color.Transparent,
),
)
},
containerColor = MaterialTheme.colorScheme.background,
containerColor = Color.Transparent,
) { padding ->
Column(
modifier = Modifier

View file

@ -2,31 +2,45 @@ package com.rada.ui.theme
import androidx.compose.ui.graphics.Color
// ── Premium dark palette ──
val PremiumBlack = Color(0xFF0A0A14)
val PremiumDark = Color(0xFF12121E)
val PremiumSurface = Color(0xFF1A1A2E)
val PremiumSurfaceVariant = Color(0xFF222238)
val PremiumBorder = Color(0xFF2A2A44)
// ── RadAmp Glass palette (see design/glass-radio-theme.css for tokens + WCAG data) ──
// Base: flat dark charcoal gradient #23232B → #1B1B22
val PremiumBlack = Color(0xFF1B1B22) // base bottom
val PremiumDark = Color(0xFF23232B) // base top
val PremiumSurface = Color(0xFF202029) // solid panel (~90% opaque base)
val PremiumSurfaceVariant = Color(0xFF262631)
val PremiumBorder = Color(0x1AFFFFFF) // glass border 10% white (decorative)
// Interactive borders need 3:1 (WCAG 1.4.11): 36% white over glass
val PremiumBorderInteractive = Color(0x5CFFFFFF)
val PremiumPurple = Color(0xFF7C3AED)
val PremiumPurple = Color(0xFF7C4DFF) // accent
val PremiumPurpleHover = Color(0xFF8E66FF)
val PremiumPurplePressed = Color(0xFF6A3DE0)
val PremiumPurpleSoft = Color(0x297C4DFF) // accent soft fill 16% — chips/active
val PremiumPurpleLight = Color(0xFFA78BFA)
// AA-safe accent TEXT on glass (7C4DFF is only 2.71:1 on glass)
val PremiumAccentText = Color(0xFFA78BFF)
val PremiumBlue = Color(0xFF3B82F6)
val PremiumCyan = Color(0xFF06B6D4)
val PremiumPink = Color(0xFFEC4899)
val PremiumText = Color(0xFFF0F0F5)
val PremiumTextSecondary = Color(0xFF9CA3AF)
val PremiumTextMuted = Color(0xFF6B7280)
// Text tokens: all pairs ≥4.5:1 on base AND on glass (measured; see demo page)
val PremiumText = Color(0xFFFFFFFF) // 13.1:1 on glass
val PremiumTextSecondary = Color(0xFFD9D9DE) // 10.5:1 on glass (body 88% white)
val PremiumTextMuted = Color(0xFF8C8C96) // 5.1:1 on base (caption floor, never dimmer)
val PremiumSuccess = Color(0xFF10B981)
val PremiumSuccess = Color(0xFF34C759)
val PremiumError = Color(0xFFEF4444)
val PremiumWarning = Color(0xFFF59E0B)
// Gradient colors for accents
val GradientStart = Color(0xFF7C3AED)
val GradientStart = Color(0xFF7C4DFF)
val GradientMid = Color(0xFF3B82F6)
val GradientEnd = Color(0xFF06B6D4)
// Base background gradient (flat charcoal, very subtle)
val BaseTop = Color(0xFF23232B)
val BaseBottom = Color(0xFF1B1B22)
// Live indicator
val LiveGreen = Color(0xFF34C759)
// Legacy terminal palette (cliamp-inspired)
val TerminalBlack = Color(0xFF000000)

View file

@ -1,16 +1,21 @@
package com.rada.ui.theme
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
private val PremiumColorScheme = darkColorScheme(
primary = PremiumPurple,
onPrimary = Color.White,
primaryContainer = PremiumPurple.copy(alpha = 0.15f),
onPrimaryContainer = PremiumPurpleLight,
onPrimary = Color.White, // 4.81:1 on #7C4DFF
primaryContainer = PremiumPurpleSoft,
onPrimaryContainer = PremiumAccentText, // AA-safe accent text on glass
secondary = PremiumBlue,
onSecondary = Color.White,
@ -22,12 +27,12 @@ private val PremiumColorScheme = darkColorScheme(
tertiaryContainer = PremiumCyan.copy(alpha = 0.15f),
onTertiaryContainer = PremiumCyan,
background = PremiumBlack,
background = BaseBottom, // charcoal gradient bottom
onBackground = PremiumText,
surface = PremiumDark,
surface = PremiumDark, // base top
onSurface = PremiumText,
surfaceVariant = PremiumSurface,
surfaceVariant = PremiumSurface, // solid panel
onSurfaceVariant = PremiumTextSecondary,
error = PremiumError,
@ -35,14 +40,46 @@ private val PremiumColorScheme = darkColorScheme(
errorContainer = PremiumError.copy(alpha = 0.15f),
onErrorContainer = PremiumError,
outline = PremiumBorder,
outline = PremiumBorder, // decorative 10% white glass border
outlineVariant = PremiumBorder.copy(alpha = 0.5f),
inverseSurface = PremiumText,
inverseOnSurface = PremiumBlack,
inversePrimary = PremiumPurpleLight,
inverseOnSurface = BaseBottom,
inversePrimary = PremiumAccentText,
surfaceTint = PremiumPurple,
)
/**
* RadAmp Glass tokens — mirrors design/glass-radio-theme.css.
* Compose has no backdrop blur without a lib, so "glass" = translucent white
* layer + inner highlight border over the charcoal base. Text never sits on
* glass alone without the 90%-opaque overlay (GlassTextSafe).
*/
object RadAmpGlass {
val bgGradient = Brush.verticalGradient(listOf(BaseTop, BaseBottom))
// very subtle violet glows, painted under content, never behind text
val glowTop = Brush.radialGradient(
colors = listOf(PremiumPurple.copy(alpha = 0.14f), Color.Transparent),
center = androidx.compose.ui.geometry.Offset(900f, 260f),
radius = 900f,
)
val surface = Color(0x0FFFFFFF) // rgba(255,255,255,0.06)
val surfaceStrong = Color(0x17FFFFFF) // hover 0.09
val border = Color(0x1AFFFFFF) // 0.10 decorative
val borderInteractive = Color(0x5CFFFFFF)// 0.36 → 3:1 on glass
val highlight = Color(0x24FFFFFF) // inset top highlight 0.14
val textSafe = Color(0xE61B1B22) // 90% opaque overlay behind text
val rCard = 20.dp
val rInput = 14.dp
}
@Composable
fun GlassBackground(content: @Composable () -> Unit) {
Box(Modifier.fillMaxSize().background(RadAmpGlass.bgGradient)) {
Box(Modifier.fillMaxSize().background(RadAmpGlass.glowTop))
content()
}
}
object RadAmpGradients {
val primary = Brush.linearGradient(
colors = listOf(GradientStart, GradientMid, GradientEnd)