Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-30 13:16:11 +03:00
parent 3398db66e2
commit 56a2eda3e1
18 changed files with 2080 additions and 631 deletions

@ -1 +1 @@
Subproject commit f4d2156236d7f77b61f26250351f6bfa72262a69
Subproject commit 27202508b606f54c276afa577a2f3e7a3da27e8b

View file

@ -29,6 +29,10 @@ import com.tangem.core.ui.haptic.DefaultHapticManager
import com.tangem.core.ui.haptic.HapticManager
import com.tangem.core.ui.haptic.VibratorHapticManager
import com.tangem.core.ui.message.EventMessageHandler
import com.tangem.core.ui.res.generated.TangemColors3
import com.tangem.core.ui.res.generated.TangemDimens3
import com.tangem.core.ui.res.generated.TangemTypography3
import com.tangem.core.ui.res.generated.lightColors3
import com.tangem.core.ui.windowsize.WindowSize
import com.tangem.core.ui.windowsize.rememberWindowSize
import com.tangem.domain.apptheme.model.AppThemeMode
@ -161,11 +165,17 @@ object TangemTheme {
@ReadOnlyComposable
get() = LocalTangemColors.current
@Deprecated("Use colors3 instead", ReplaceWith("TangemTheme.colors3"))
val colors2: TangemColors2
@Composable
@ReadOnlyComposable
get() = LocalTangemColors2.current
val colors3: TangemColors3
@Composable
@ReadOnlyComposable
get() = LocalTangemColors3.current
val typography: TangemTypography
@Composable
@ReadOnlyComposable
@ -176,6 +186,11 @@ object TangemTheme {
@ReadOnlyComposable
get() = TangemTypography2(InterFamily)
val typography3: TangemTypography3
@Composable
@ReadOnlyComposable
get() = LocalTangemTypography3.current
val dimens: TangemDimens
@Composable
@ReadOnlyComposable
@ -186,6 +201,11 @@ object TangemTheme {
@ReadOnlyComposable
get() = LocalTangemDimens2.current
val dimens3: TangemDimens3
@Composable
@ReadOnlyComposable
get() = LocalTangemDimens3.current
val shapes: TangemShapes
@Composable
@ReadOnlyComposable
@ -366,6 +386,10 @@ internal val LocalTangemColors2 = staticCompositionLocalOf<TangemColors2> {
error("No TangemColors2 provided")
}
internal val LocalTangemColors3 = staticCompositionLocalOf {
lightColors3()
}
internal val LocalTangemTypography = staticCompositionLocalOf {
TangemTypography(RobotoFamily)
}
@ -382,6 +406,14 @@ private val LocalTangemDimens2 = staticCompositionLocalOf {
TangemDimens2()
}
internal val LocalTangemDimens3 = staticCompositionLocalOf {
TangemDimens3()
}
internal val LocalTangemTypography3 = staticCompositionLocalOf {
TangemTypography3(InterFamily)
}
private val LocalTangemShapes = staticCompositionLocalOf<TangemShapes> {
error("No TangemShapes provided")
}

View file

@ -10,6 +10,10 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import com.tangem.core.ui.components.haze.ProvideHaze
import com.tangem.core.ui.components.haze.hazeSourceTangem
import com.tangem.core.ui.res.generated.TangemDimens3
import com.tangem.core.ui.res.generated.TangemTypography3
import com.tangem.core.ui.res.generated.darkColors3
import com.tangem.core.ui.res.generated.lightColors3
/**
* Provides additional theming for redesigned components.
@ -21,17 +25,30 @@ fun TangemThemeRedesign(content: @Composable () -> Unit) {
val themeColors = if (LocalIsInDarkTheme.current) darkThemeColors() else lightThemeColors(redesign = true)
val rememberedColors = remember { themeColors }
.apply { update(themeColors) }
val themeColors3 = if (LocalIsInDarkTheme.current) darkColors3() else lightColors3()
val rememberedColors3 = remember { themeColors3 }
.apply { update(themeColors3) }
val rootBackgroundColor = rememberedColors.background.secondary
val tangemDimens3 = remember { TangemDimens3() }
val tangemTypography3 = remember { TangemTypography3(InterFamily) }
val tangemTypography2 = remember { TangemTypography2(InterFamily) }
val tangemTypography = remember { TangemTypography(InterFamily) }
MaterialTheme(
colorScheme = tangemColorScheme(colors = themeColors),
colorScheme = tangemColorScheme(colors = rememberedColors),
) {
CompositionLocalProvider(
LocalRedesignEnabled provides true,
LocalTangemColors provides themeColors,
LocalTangemColors provides rememberedColors,
LocalTangemColors2 provides if (LocalIsInDarkTheme.current) darkThemeColors2() else lightThemeColors2(),
LocalTangemTypography2 provides TangemTypography2(InterFamily),
LocalTangemTypography provides TangemTypography(InterFamily),
LocalTangemColors3 provides rememberedColors3,
LocalTangemDimens3 provides tangemDimens3,
LocalTangemTypography3 provides tangemTypography3,
LocalTangemTypography2 provides tangemTypography2,
LocalTangemTypography provides tangemTypography,
LocalRootBackgroundColor provides remember(rootBackgroundColor) { mutableStateOf(rootBackgroundColor) },
) {
CompositionLocalProvider(

View file

@ -1 +1 @@
ddef7c44794c12a02c136fcc17f01fe51b6372d33a6a7eda4913d2d1b1f86203
84387e888f54e5056380c38e077962bdfa4a32cfca194d822c13aa7e35661968

View file

@ -0,0 +1,149 @@
@file:Suppress("all")
package com.tangem.core.ui.res.generated
import androidx.compose.ui.graphics.Color
/**
* Auto-generated from design tokens. Do not edit manually.
*/
internal object TangemColorPalette {
object Base {
val black = Color(0xFF000000)
val white = Color(0xFFFFFFFF)
}
object Neutral {
val `5` = Color(0xFFF4F4F4)
val `10` = Color(0xFFEAEAEA)
val `20` = Color(0xFFD0D0D0)
val `30` = Color(0xFFB5B5B5)
val `40` = Color(0xFF989898)
val `50` = Color(0xFF838383)
val `60` = Color(0xFF6F6F6F)
val `70` = Color(0xFF4A4A4A)
val `80` = Color(0xFF2C2C2C)
val `90` = Color(0xFF1B1B1B)
val `95` = Color(0xFF0F0F0F)
}
object Blue {
val `5` = Color(0xFFE9F7FF)
val `10` = Color(0xFFDBF1FF)
val `20` = Color(0xFF98D7FF)
val `30` = Color(0xFF5EBDF9)
val `40` = Color(0xFF109FF0)
val `50` = Color(0xFF0090F9)
val `60` = Color(0xFF0077E1)
val `70` = Color(0xFF0C58AF)
val `80` = Color(0xFF143C70)
val `90` = Color(0xFF1B304A)
val `95` = Color(0xFF101C2C)
}
object Violet {
val `5` = Color(0xFFF6F1FF)
val `10` = Color(0xFFEEE7FD)
val `20` = Color(0xFFDAC8FB)
val `30` = Color(0xFFC5A5FC)
val `40` = Color(0xFFB07BFD)
val `50` = Color(0xFFA967FD)
val `60` = Color(0xFF9258DC)
val `70` = Color(0xFF67419B)
val `80` = Color(0xFF473068)
val `90` = Color(0xFF332846)
val `95` = Color(0xFF201B2A)
}
object Red {
val `5` = Color(0xFFFFF0F7)
val `10` = Color(0xFFFFE8EC)
val `20` = Color(0xFFFFC0C3)
val `30` = Color(0xFFFF979D)
val `40` = Color(0xFFFF5E66)
val `50` = Color(0xFFFE4142)
val `60` = Color(0xFFE12C2E)
val `70` = Color(0xFF9E2729)
val `80` = Color(0xFF6D2323)
val `90` = Color(0xFF4C2121)
val `95` = Color(0xFF2B1818)
}
object Orange {
val `5` = Color(0xFFFFF0EA)
val `10` = Color(0xFFFFE5DB)
val `20` = Color(0xFFFFC3AD)
val `30` = Color(0xFFFF9976)
val `40` = Color(0xFFFA6931)
val `50` = Color(0xFFF25508)
val `60` = Color(0xFFD3480E)
val `70` = Color(0xFF953715)
val `80` = Color(0xFF652A18)
val `90` = Color(0xFF43251A)
val `95` = Color(0xFF2A1A13)
}
object Green {
val `5` = Color(0xFFEBF6ED)
val `10` = Color(0xFFDCFBE3)
val `20` = Color(0xFF9EE1AB)
val `30` = Color(0xFF64C973)
val `40` = Color(0xFF2DAE3B)
val `50` = Color(0xFF2DA30D)
val `60` = Color(0xFF208900)
val `70` = Color(0xFF1E6110)
val `80` = Color(0xFF1C4415)
val `90` = Color(0xFF1D3319)
val `95` = Color(0xFF162114)
}
object Yellow {
val `5` = Color(0xFFFAF3E5)
val `10` = Color(0xFFFEF5E5)
val `20` = Color(0xFFF7CA75)
val `30` = Color(0xFFF4B42F)
val `40` = Color(0xFFEFA210)
val `50` = Color(0xFFE68A03)
val `60` = Color(0xFFCD7A11)
val `70` = Color(0xFF965001)
val `80` = Color(0xFF573414)
val `90` = Color(0xFF3D2918)
val `95` = Color(0xFF241B13)
}
object Opaque {
object BaseBlack {
val `5` = Color(0x0D000000)
val `10` = Color(0x1A000000)
val `15` = Color(0x26000000)
val `20` = Color(0x33000000)
val `25` = Color(0x40000000)
val `30` = Color(0x4D000000)
val `40` = Color(0x66000000)
val `50` = Color(0x80000000)
val `60` = Color(0x99000000)
val `80` = Color(0xCC000000)
}
object BaseWhite {
val `5` = Color(0x0DFFFFFF)
val `10` = Color(0x1AFFFFFF)
val `15` = Color(0x26FFFFFF)
val `20` = Color(0x33FFFFFF)
val `25` = Color(0x40FFFFFF)
val `30` = Color(0x4DFFFFFF)
val `40` = Color(0x66FFFFFF)
val `50` = Color(0x80FFFFFF)
val `60` = Color(0x99FFFFFF)
val `80` = Color(0xCCFFFFFF)
}
object Neutral95 {
val `5` = Color(0x0D0F0F0F)
val `10` = Color(0x1A0F0F0F)
val `15` = Color(0x260F0F0F)
val `40` = Color(0x660F0F0F)
val `60` = Color(0x990F0F0F)
}
}
}

View file

@ -0,0 +1,714 @@
@file:Suppress("all")
package com.tangem.core.ui.res.generated
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.Color
/**
* Auto-generated from design tokens. Do not edit manually.
*/
@Stable
class TangemColors3 internal constructor(
val text: Text,
val bg: Bg,
val icon: Icon,
val border: Border,
val overlay: Overlay,
val interaction: Interaction,
val material: Material,
) {
@Stable
class Text internal constructor(
primary: Color,
secondary: Color,
tertiary: Color,
brand: Color,
val staticLight: StaticLight,
val staticDark: StaticDark,
val inverse: Inverse,
val status: Status,
val accent: Accent,
) {
var primary by mutableStateOf(primary)
private set
var secondary by mutableStateOf(secondary)
private set
var tertiary by mutableStateOf(tertiary)
private set
var brand by mutableStateOf(brand)
private set
@Stable
class StaticLight internal constructor(
primary: Color,
secondary: Color,
tertiary: Color,
) {
var primary by mutableStateOf(primary)
private set
var secondary by mutableStateOf(secondary)
private set
var tertiary by mutableStateOf(tertiary)
private set
fun update(other: StaticLight) {
primary = other.primary
secondary = other.secondary
tertiary = other.tertiary
}
}
@Stable
class StaticDark internal constructor(
primary: Color,
secondary: Color,
tertiary: Color,
) {
var primary by mutableStateOf(primary)
private set
var secondary by mutableStateOf(secondary)
private set
var tertiary by mutableStateOf(tertiary)
private set
fun update(other: StaticDark) {
primary = other.primary
secondary = other.secondary
tertiary = other.tertiary
}
}
@Stable
class Inverse internal constructor(
primary: Color,
secondary: Color,
tertiary: Color,
) {
var primary by mutableStateOf(primary)
private set
var secondary by mutableStateOf(secondary)
private set
var tertiary by mutableStateOf(tertiary)
private set
fun update(other: Inverse) {
primary = other.primary
secondary = other.secondary
tertiary = other.tertiary
}
}
@Stable
class Status internal constructor(
success: Color,
error: Color,
warning: Color,
info: Color,
) {
var success by mutableStateOf(success)
private set
var error by mutableStateOf(error)
private set
var warning by mutableStateOf(warning)
private set
var info by mutableStateOf(info)
private set
fun update(other: Status) {
success = other.success
error = other.error
warning = other.warning
info = other.info
}
}
@Stable
class Accent internal constructor(
blue: Color,
violet: Color,
red: Color,
orange: Color,
yellow: Color,
green: Color,
) {
var blue by mutableStateOf(blue)
private set
var violet by mutableStateOf(violet)
private set
var red by mutableStateOf(red)
private set
var orange by mutableStateOf(orange)
private set
var yellow by mutableStateOf(yellow)
private set
var green by mutableStateOf(green)
private set
fun update(other: Accent) {
blue = other.blue
violet = other.violet
red = other.red
orange = other.orange
yellow = other.yellow
green = other.green
}
}
fun update(other: Text) {
primary = other.primary
secondary = other.secondary
tertiary = other.tertiary
brand = other.brand
staticLight.update(other.staticLight)
staticDark.update(other.staticDark)
inverse.update(other.inverse)
status.update(other.status)
accent.update(other.accent)
}
}
@Stable
class Bg internal constructor(
primary: Color,
secondary: Color,
tertiary: Color,
brand: Color,
inverse: Color,
base: Color,
disabled: Color,
val opaque: Opaque,
val status: Status,
val accent: Accent,
) {
var primary by mutableStateOf(primary)
private set
var secondary by mutableStateOf(secondary)
private set
var tertiary by mutableStateOf(tertiary)
private set
var brand by mutableStateOf(brand)
private set
var inverse by mutableStateOf(inverse)
private set
var base by mutableStateOf(base)
private set
var disabled by mutableStateOf(disabled)
private set
@Stable
class Opaque internal constructor(
primary: Color,
secondary: Color,
) {
var primary by mutableStateOf(primary)
private set
var secondary by mutableStateOf(secondary)
private set
fun update(other: Opaque) {
primary = other.primary
secondary = other.secondary
}
}
@Stable
class Status internal constructor(
success: Color,
successSubtle: Color,
error: Color,
errorSubtle: Color,
warning: Color,
warningSubtle: Color,
info: Color,
infoSubtle: Color,
) {
var success by mutableStateOf(success)
private set
var successSubtle by mutableStateOf(successSubtle)
private set
var error by mutableStateOf(error)
private set
var errorSubtle by mutableStateOf(errorSubtle)
private set
var warning by mutableStateOf(warning)
private set
var warningSubtle by mutableStateOf(warningSubtle)
private set
var info by mutableStateOf(info)
private set
var infoSubtle by mutableStateOf(infoSubtle)
private set
fun update(other: Status) {
success = other.success
successSubtle = other.successSubtle
error = other.error
errorSubtle = other.errorSubtle
warning = other.warning
warningSubtle = other.warningSubtle
info = other.info
infoSubtle = other.infoSubtle
}
}
@Stable
class Accent internal constructor(
blue: Color,
violet: Color,
red: Color,
orange: Color,
yellow: Color,
green: Color,
) {
var blue by mutableStateOf(blue)
private set
var violet by mutableStateOf(violet)
private set
var red by mutableStateOf(red)
private set
var orange by mutableStateOf(orange)
private set
var yellow by mutableStateOf(yellow)
private set
var green by mutableStateOf(green)
private set
fun update(other: Accent) {
blue = other.blue
violet = other.violet
red = other.red
orange = other.orange
yellow = other.yellow
green = other.green
}
}
fun update(other: Bg) {
primary = other.primary
secondary = other.secondary
tertiary = other.tertiary
brand = other.brand
inverse = other.inverse
base = other.base
disabled = other.disabled
opaque.update(other.opaque)
status.update(other.status)
accent.update(other.accent)
}
}
@Stable
class Icon internal constructor(
primary: Color,
secondary: Color,
tertiary: Color,
brand: Color,
staticLight: Color,
staticDark: Color,
inverse: Color,
val status: Status,
val accent: Accent,
) {
var primary by mutableStateOf(primary)
private set
var secondary by mutableStateOf(secondary)
private set
var tertiary by mutableStateOf(tertiary)
private set
var brand by mutableStateOf(brand)
private set
var staticLight by mutableStateOf(staticLight)
private set
var staticDark by mutableStateOf(staticDark)
private set
var inverse by mutableStateOf(inverse)
private set
@Stable
class Status internal constructor(
success: Color,
error: Color,
warning: Color,
info: Color,
) {
var success by mutableStateOf(success)
private set
var error by mutableStateOf(error)
private set
var warning by mutableStateOf(warning)
private set
var info by mutableStateOf(info)
private set
fun update(other: Status) {
success = other.success
error = other.error
warning = other.warning
info = other.info
}
}
@Stable
class Accent internal constructor(
blue: Color,
violet: Color,
red: Color,
orange: Color,
yellow: Color,
green: Color,
) {
var blue by mutableStateOf(blue)
private set
var violet by mutableStateOf(violet)
private set
var red by mutableStateOf(red)
private set
var orange by mutableStateOf(orange)
private set
var yellow by mutableStateOf(yellow)
private set
var green by mutableStateOf(green)
private set
fun update(other: Accent) {
blue = other.blue
violet = other.violet
red = other.red
orange = other.orange
yellow = other.yellow
green = other.green
}
}
fun update(other: Icon) {
primary = other.primary
secondary = other.secondary
tertiary = other.tertiary
brand = other.brand
staticLight = other.staticLight
staticDark = other.staticDark
inverse = other.inverse
status.update(other.status)
accent.update(other.accent)
}
}
@Stable
class Border internal constructor(
primary: Color,
secondary: Color,
tertiary: Color,
brand: Color,
val inverse: Inverse,
val status: Status,
val accent: Accent,
) {
var primary by mutableStateOf(primary)
private set
var secondary by mutableStateOf(secondary)
private set
var tertiary by mutableStateOf(tertiary)
private set
var brand by mutableStateOf(brand)
private set
@Stable
class Inverse internal constructor(
primary: Color,
secondary: Color,
tertiary: Color,
) {
var primary by mutableStateOf(primary)
private set
var secondary by mutableStateOf(secondary)
private set
var tertiary by mutableStateOf(tertiary)
private set
fun update(other: Inverse) {
primary = other.primary
secondary = other.secondary
tertiary = other.tertiary
}
}
@Stable
class Status internal constructor(
success: Color,
successSubtle: Color,
error: Color,
errorSubtle: Color,
warning: Color,
warningSubtle: Color,
info: Color,
infoSubtle: Color,
) {
var success by mutableStateOf(success)
private set
var successSubtle by mutableStateOf(successSubtle)
private set
var error by mutableStateOf(error)
private set
var errorSubtle by mutableStateOf(errorSubtle)
private set
var warning by mutableStateOf(warning)
private set
var warningSubtle by mutableStateOf(warningSubtle)
private set
var info by mutableStateOf(info)
private set
var infoSubtle by mutableStateOf(infoSubtle)
private set
fun update(other: Status) {
success = other.success
successSubtle = other.successSubtle
error = other.error
errorSubtle = other.errorSubtle
warning = other.warning
warningSubtle = other.warningSubtle
info = other.info
infoSubtle = other.infoSubtle
}
}
@Stable
class Accent internal constructor(
blue: Color,
violet: Color,
red: Color,
orange: Color,
yellow: Color,
green: Color,
) {
var blue by mutableStateOf(blue)
private set
var violet by mutableStateOf(violet)
private set
var red by mutableStateOf(red)
private set
var orange by mutableStateOf(orange)
private set
var yellow by mutableStateOf(yellow)
private set
var green by mutableStateOf(green)
private set
fun update(other: Accent) {
blue = other.blue
violet = other.violet
red = other.red
orange = other.orange
yellow = other.yellow
green = other.green
}
}
fun update(other: Border) {
primary = other.primary
secondary = other.secondary
tertiary = other.tertiary
brand = other.brand
inverse.update(other.inverse)
status.update(other.status)
accent.update(other.accent)
}
}
@Stable
class Overlay internal constructor(
modal: Color,
) {
var modal by mutableStateOf(modal)
private set
fun update(other: Overlay) {
modal = other.modal
}
}
@Stable
class Interaction internal constructor(
pressStaticLight: Color,
pressStaticDark: Color,
val press: Press,
val focusRing: FocusRing,
) {
var pressStaticLight by mutableStateOf(pressStaticLight)
private set
var pressStaticDark by mutableStateOf(pressStaticDark)
private set
@Stable
class Press internal constructor(
default: Color,
inverse: Color,
) {
var default by mutableStateOf(default)
private set
var inverse by mutableStateOf(inverse)
private set
fun update(other: Press) {
default = other.default
inverse = other.inverse
}
}
@Stable
class FocusRing internal constructor(
default: Color,
brand: Color,
) {
var default by mutableStateOf(default)
private set
var brand by mutableStateOf(brand)
private set
fun update(other: FocusRing) {
default = other.default
brand = other.brand
}
}
fun update(other: Interaction) {
pressStaticLight = other.pressStaticLight
pressStaticDark = other.pressStaticDark
press.update(other.press)
focusRing.update(other.focusRing)
}
}
@Stable
class Material internal constructor(
val tint: Tint,
val fill: Fill,
val lighten: Lighten,
val softLight: SoftLight,
val border: Border,
) {
@Stable
class Tint internal constructor(
glass: Color,
blur: Color,
solid: Color,
) {
var glass by mutableStateOf(glass)
private set
var blur by mutableStateOf(blur)
private set
var solid by mutableStateOf(solid)
private set
fun update(other: Tint) {
glass = other.glass
blur = other.blur
solid = other.solid
}
}
@Stable
class Fill internal constructor(
glass: Color,
blur: Color,
solid: Color,
) {
var glass by mutableStateOf(glass)
private set
var blur by mutableStateOf(blur)
private set
var solid by mutableStateOf(solid)
private set
fun update(other: Fill) {
glass = other.glass
blur = other.blur
solid = other.solid
}
}
@Stable
class Lighten internal constructor(
glass: Color,
blur: Color,
solid: Color,
) {
var glass by mutableStateOf(glass)
private set
var blur by mutableStateOf(blur)
private set
var solid by mutableStateOf(solid)
private set
fun update(other: Lighten) {
glass = other.glass
blur = other.blur
solid = other.solid
}
}
@Stable
class SoftLight internal constructor(
glass: Color,
blur: Color,
solid: Color,
) {
var glass by mutableStateOf(glass)
private set
var blur by mutableStateOf(blur)
private set
var solid by mutableStateOf(solid)
private set
fun update(other: SoftLight) {
glass = other.glass
blur = other.blur
solid = other.solid
}
}
@Stable
class Border internal constructor(
start: Color,
mid: Color,
end: Color,
) {
var start by mutableStateOf(start)
private set
var mid by mutableStateOf(mid)
private set
var end by mutableStateOf(end)
private set
fun update(other: Border) {
start = other.start
mid = other.mid
end = other.end
}
}
fun update(other: Material) {
tint.update(other.tint)
fill.update(other.fill)
lighten.update(other.lighten)
softLight.update(other.softLight)
border.update(other.border)
}
}
fun update(other: TangemColors3) {
text.update(other.text)
bg.update(other.bg)
icon.update(other.icon)
border.update(other.border)
overlay.update(other.overlay)
interaction.update(other.interaction)
material.update(other.material)
}
}

View file

@ -0,0 +1,173 @@
@file:Suppress("all")
package com.tangem.core.ui.res.generated
import androidx.compose.ui.graphics.Color
/**
* Auto-generated from design tokens. Do not edit manually.
* Theme: Dark
*/
internal fun darkColors3() =
TangemColors3(
text = TangemColors3.Text(
primary = TangemColorPalette.Base.white,
secondary = TangemColorPalette.Opaque.BaseWhite.`60`,
tertiary = TangemColorPalette.Opaque.BaseWhite.`30`,
brand = TangemColorPalette.Blue.`50`,
staticLight = TangemColors3.Text.StaticLight(
primary = TangemColorPalette.Base.black,
secondary = TangemColorPalette.Opaque.BaseBlack.`60`,
tertiary = TangemColorPalette.Opaque.BaseBlack.`40`,
),
staticDark = TangemColors3.Text.StaticDark(
primary = TangemColorPalette.Base.white,
secondary = TangemColorPalette.Opaque.BaseWhite.`60`,
tertiary = TangemColorPalette.Opaque.BaseWhite.`30`,
),
inverse = TangemColors3.Text.Inverse(
primary = TangemColorPalette.Neutral.`95`,
secondary = TangemColorPalette.Opaque.Neutral95.`60`,
tertiary = TangemColorPalette.Opaque.Neutral95.`40`,
),
status = TangemColors3.Text.Status(
success = TangemColorPalette.Green.`40`,
error = TangemColorPalette.Red.`40`,
warning = TangemColorPalette.Yellow.`40`,
info = TangemColorPalette.Blue.`40`,
),
accent = TangemColors3.Text.Accent(
blue = TangemColorPalette.Blue.`40`,
violet = TangemColorPalette.Violet.`40`,
red = TangemColorPalette.Red.`40`,
orange = TangemColorPalette.Orange.`40`,
yellow = TangemColorPalette.Yellow.`40`,
green = TangemColorPalette.Green.`40`,
),
),
bg = TangemColors3.Bg(
primary = TangemColorPalette.Neutral.`95`,
secondary = TangemColorPalette.Neutral.`90`,
tertiary = TangemColorPalette.Neutral.`80`,
brand = TangemColorPalette.Blue.`50`,
inverse = TangemColorPalette.Neutral.`5`,
base = TangemColorPalette.Base.black,
disabled = TangemColorPalette.Neutral.`70`,
opaque = TangemColors3.Bg.Opaque(
primary = TangemColorPalette.Opaque.BaseWhite.`5`,
secondary = TangemColorPalette.Opaque.BaseWhite.`10`,
),
status = TangemColors3.Bg.Status(
success = TangemColorPalette.Green.`50`,
successSubtle = TangemColorPalette.Green.`90`,
error = TangemColorPalette.Red.`50`,
errorSubtle = TangemColorPalette.Red.`90`,
warning = TangemColorPalette.Yellow.`50`,
warningSubtle = TangemColorPalette.Yellow.`90`,
info = TangemColorPalette.Blue.`50`,
infoSubtle = TangemColorPalette.Blue.`90`,
),
accent = TangemColors3.Bg.Accent(
blue = TangemColorPalette.Blue.`50`,
violet = TangemColorPalette.Violet.`50`,
red = TangemColorPalette.Red.`50`,
orange = TangemColorPalette.Orange.`50`,
yellow = TangemColorPalette.Yellow.`50`,
green = TangemColorPalette.Green.`50`,
),
),
icon = TangemColors3.Icon(
primary = TangemColorPalette.Base.white,
secondary = TangemColorPalette.Opaque.BaseWhite.`60`,
tertiary = TangemColorPalette.Opaque.BaseWhite.`30`,
brand = TangemColorPalette.Blue.`50`,
staticLight = TangemColorPalette.Base.black,
staticDark = TangemColorPalette.Base.white,
inverse = TangemColorPalette.Neutral.`95`,
status = TangemColors3.Icon.Status(
success = TangemColorPalette.Green.`40`,
error = TangemColorPalette.Red.`40`,
warning = TangemColorPalette.Yellow.`40`,
info = TangemColorPalette.Blue.`40`,
),
accent = TangemColors3.Icon.Accent(
blue = TangemColorPalette.Blue.`40`,
violet = TangemColorPalette.Violet.`40`,
red = TangemColorPalette.Red.`40`,
orange = TangemColorPalette.Orange.`40`,
yellow = TangemColorPalette.Yellow.`40`,
green = TangemColorPalette.Green.`40`,
),
),
border = TangemColors3.Border(
primary = TangemColorPalette.Opaque.BaseWhite.`5`,
secondary = TangemColorPalette.Opaque.BaseWhite.`10`,
tertiary = TangemColorPalette.Opaque.BaseWhite.`20`,
brand = TangemColorPalette.Blue.`50`,
inverse = TangemColors3.Border.Inverse(
primary = TangemColorPalette.Opaque.BaseBlack.`5`,
secondary = TangemColorPalette.Opaque.BaseBlack.`10`,
tertiary = TangemColorPalette.Opaque.BaseBlack.`20`,
),
status = TangemColors3.Border.Status(
success = TangemColorPalette.Green.`40`,
successSubtle = TangemColorPalette.Green.`80`,
error = TangemColorPalette.Red.`40`,
errorSubtle = TangemColorPalette.Red.`80`,
warning = TangemColorPalette.Yellow.`40`,
warningSubtle = TangemColorPalette.Yellow.`80`,
info = TangemColorPalette.Blue.`40`,
infoSubtle = TangemColorPalette.Blue.`80`,
),
accent = TangemColors3.Border.Accent(
blue = TangemColorPalette.Blue.`40`,
violet = TangemColorPalette.Violet.`40`,
red = TangemColorPalette.Red.`40`,
orange = TangemColorPalette.Orange.`40`,
yellow = TangemColorPalette.Yellow.`40`,
green = TangemColorPalette.Green.`40`,
),
),
overlay = TangemColors3.Overlay(
modal = TangemColorPalette.Opaque.BaseBlack.`80`,
),
interaction = TangemColors3.Interaction(
pressStaticLight = TangemColorPalette.Opaque.BaseBlack.`10`,
pressStaticDark = TangemColorPalette.Opaque.BaseWhite.`10`,
press = TangemColors3.Interaction.Press(
default = TangemColorPalette.Opaque.BaseWhite.`10`,
inverse = TangemColorPalette.Opaque.BaseBlack.`10`,
),
focusRing = TangemColors3.Interaction.FocusRing(
default = TangemColorPalette.Neutral.`5`,
brand = TangemColorPalette.Blue.`50`,
),
),
material = TangemColors3.Material(
tint = TangemColors3.Material.Tint(
glass = Color(0x662C2C2C),
blur = Color(0x00000000),
solid = Color(0x1AFFFFFF),
),
fill = TangemColors3.Material.Fill(
glass = Color(0x00000000),
blur = Color(0x1AFFFFFF),
solid = Color(0xE62C2C2C),
),
lighten = TangemColors3.Material.Lighten(
glass = Color(0x33181818),
blur = Color(0x00000000),
solid = Color(0x00000000),
),
softLight = TangemColors3.Material.SoftLight(
glass = Color(0x1A000000),
blur = Color(0x00000000),
solid = Color(0x00000000),
),
border = TangemColors3.Material.Border(
start = Color(0x33FFFFFF),
mid = Color(0x00FFFFFF),
end = Color(0x1AFFFFFF),
),
),
)

View file

@ -0,0 +1,173 @@
@file:Suppress("all")
package com.tangem.core.ui.res.generated
import androidx.compose.ui.graphics.Color
/**
* Auto-generated from design tokens. Do not edit manually.
* Theme: Light
*/
internal fun lightColors3() =
TangemColors3(
text = TangemColors3.Text(
primary = TangemColorPalette.Neutral.`95`,
secondary = TangemColorPalette.Opaque.Neutral95.`60`,
tertiary = TangemColorPalette.Opaque.Neutral95.`40`,
brand = TangemColorPalette.Blue.`50`,
staticLight = TangemColors3.Text.StaticLight(
primary = TangemColorPalette.Base.black,
secondary = TangemColorPalette.Opaque.BaseBlack.`60`,
tertiary = TangemColorPalette.Opaque.BaseBlack.`40`,
),
staticDark = TangemColors3.Text.StaticDark(
primary = TangemColorPalette.Base.white,
secondary = TangemColorPalette.Opaque.BaseWhite.`60`,
tertiary = TangemColorPalette.Opaque.BaseWhite.`30`,
),
inverse = TangemColors3.Text.Inverse(
primary = TangemColorPalette.Base.white,
secondary = TangemColorPalette.Opaque.BaseWhite.`60`,
tertiary = TangemColorPalette.Opaque.BaseWhite.`30`,
),
status = TangemColors3.Text.Status(
success = TangemColorPalette.Green.`50`,
error = TangemColorPalette.Red.`50`,
warning = TangemColorPalette.Yellow.`50`,
info = TangemColorPalette.Blue.`50`,
),
accent = TangemColors3.Text.Accent(
blue = TangemColorPalette.Blue.`50`,
violet = TangemColorPalette.Violet.`50`,
red = TangemColorPalette.Red.`50`,
orange = TangemColorPalette.Orange.`50`,
yellow = TangemColorPalette.Yellow.`50`,
green = TangemColorPalette.Green.`50`,
),
),
bg = TangemColors3.Bg(
primary = TangemColorPalette.Neutral.`5`,
secondary = TangemColorPalette.Base.white,
tertiary = TangemColorPalette.Neutral.`10`,
brand = TangemColorPalette.Blue.`50`,
inverse = TangemColorPalette.Neutral.`95`,
base = TangemColorPalette.Neutral.`5`,
disabled = TangemColorPalette.Neutral.`20`,
opaque = TangemColors3.Bg.Opaque(
primary = TangemColorPalette.Opaque.Neutral95.`5`,
secondary = TangemColorPalette.Opaque.Neutral95.`10`,
),
status = TangemColors3.Bg.Status(
success = TangemColorPalette.Green.`50`,
successSubtle = TangemColorPalette.Green.`10`,
error = TangemColorPalette.Red.`50`,
errorSubtle = TangemColorPalette.Red.`10`,
warning = TangemColorPalette.Yellow.`50`,
warningSubtle = TangemColorPalette.Yellow.`10`,
info = TangemColorPalette.Blue.`50`,
infoSubtle = TangemColorPalette.Blue.`10`,
),
accent = TangemColors3.Bg.Accent(
blue = TangemColorPalette.Blue.`50`,
violet = TangemColorPalette.Violet.`50`,
red = TangemColorPalette.Red.`50`,
orange = TangemColorPalette.Orange.`50`,
yellow = TangemColorPalette.Yellow.`50`,
green = TangemColorPalette.Green.`50`,
),
),
icon = TangemColors3.Icon(
primary = TangemColorPalette.Neutral.`95`,
secondary = TangemColorPalette.Opaque.Neutral95.`60`,
tertiary = TangemColorPalette.Opaque.Neutral95.`40`,
brand = TangemColorPalette.Blue.`50`,
staticLight = TangemColorPalette.Base.black,
staticDark = TangemColorPalette.Base.white,
inverse = TangemColorPalette.Base.white,
status = TangemColors3.Icon.Status(
success = TangemColorPalette.Green.`50`,
error = TangemColorPalette.Red.`50`,
warning = TangemColorPalette.Yellow.`50`,
info = TangemColorPalette.Blue.`50`,
),
accent = TangemColors3.Icon.Accent(
blue = TangemColorPalette.Blue.`50`,
violet = TangemColorPalette.Violet.`50`,
red = TangemColorPalette.Red.`50`,
orange = TangemColorPalette.Orange.`50`,
yellow = TangemColorPalette.Yellow.`50`,
green = TangemColorPalette.Green.`50`,
),
),
border = TangemColors3.Border(
primary = TangemColorPalette.Opaque.BaseBlack.`5`,
secondary = TangemColorPalette.Opaque.BaseBlack.`10`,
tertiary = TangemColorPalette.Opaque.BaseBlack.`20`,
brand = TangemColorPalette.Blue.`50`,
inverse = TangemColors3.Border.Inverse(
primary = TangemColorPalette.Opaque.BaseWhite.`5`,
secondary = TangemColorPalette.Opaque.BaseWhite.`10`,
tertiary = TangemColorPalette.Opaque.BaseWhite.`20`,
),
status = TangemColors3.Border.Status(
success = TangemColorPalette.Green.`50`,
successSubtle = TangemColorPalette.Green.`20`,
error = TangemColorPalette.Red.`50`,
errorSubtle = TangemColorPalette.Red.`20`,
warning = TangemColorPalette.Yellow.`50`,
warningSubtle = TangemColorPalette.Yellow.`20`,
info = TangemColorPalette.Blue.`50`,
infoSubtle = TangemColorPalette.Blue.`20`,
),
accent = TangemColors3.Border.Accent(
blue = TangemColorPalette.Blue.`50`,
violet = TangemColorPalette.Violet.`50`,
red = TangemColorPalette.Red.`50`,
orange = TangemColorPalette.Orange.`50`,
yellow = TangemColorPalette.Yellow.`50`,
green = TangemColorPalette.Green.`50`,
),
),
overlay = TangemColors3.Overlay(
modal = TangemColorPalette.Opaque.BaseBlack.`60`,
),
interaction = TangemColors3.Interaction(
pressStaticLight = TangemColorPalette.Opaque.BaseBlack.`10`,
pressStaticDark = TangemColorPalette.Opaque.BaseWhite.`10`,
press = TangemColors3.Interaction.Press(
default = TangemColorPalette.Opaque.BaseBlack.`10`,
inverse = TangemColorPalette.Opaque.BaseWhite.`10`,
),
focusRing = TangemColors3.Interaction.FocusRing(
default = TangemColorPalette.Neutral.`95`,
brand = TangemColorPalette.Blue.`50`,
),
),
material = TangemColors3.Material(
tint = TangemColors3.Material.Tint(
glass = Color(0x00000000),
blur = Color(0x00000000),
solid = Color(0x1AFFFFFF),
),
fill = TangemColors3.Material.Fill(
glass = Color(0x00000000),
blur = Color(0x99FFFFFF),
solid = Color(0xE6FFFFFF),
),
lighten = TangemColors3.Material.Lighten(
glass = Color(0x80F7F7F7),
blur = Color(0x00000000),
solid = Color(0x00000000),
),
softLight = TangemColors3.Material.SoftLight(
glass = Color(0x33000000),
blur = Color(0x00000000),
solid = Color(0x00000000),
),
border = TangemColors3.Material.Border(
start = Color(0x26000000),
mid = Color(0x00000000),
end = Color(0x1A000000),
),
),
)

View file

@ -1,118 +0,0 @@
package com.tangem.core.ui.res.generated
import androidx.compose.ui.graphics.Color
/**
* Auto-generated from design tokens. Do not edit manually.
* Theme: Dark
*/
internal class TangemDarkColorTokens {
// color.text
val colorTextPrimary = Color(0xFFFFFFFF)
val colorTextSecondary = Color(0x99FFFFFF)
val colorTextTertiary = Color(0x4DFFFFFF)
val colorTextBrand = Color(0xFF0090F9)
val colorTextStaticLightPrimary = Color(0xFF000000)
val colorTextStaticLightSecondary = Color(0x99000000)
val colorTextStaticLightTertiary = Color(0x66000000)
val colorTextStaticDarkPrimary = Color(0xFFFFFFFF)
val colorTextStaticDarkSecondary = Color(0x99FFFFFF)
val colorTextStaticDarkTertiary = Color(0x4DFFFFFF)
val colorTextInversePrimary = Color(0xFF0F0F0F)
val colorTextInverseSecondary = Color(0x990F0F0F)
val colorTextInverseTertiary = Color(0x660F0F0F)
val colorTextStatusSuccess = Color(0xFF2DAE3B)
val colorTextStatusError = Color(0xFFFF5E66)
val colorTextStatusWarning = Color(0xFFEFA210)
val colorTextStatusInfo = Color(0xFF109FF0)
val colorTextAccentBlue = Color(0xFF109FF0)
val colorTextAccentViolet = Color(0xFFB07BFD)
val colorTextAccentRed = Color(0xFFFF5E66)
val colorTextAccentOrange = Color(0xFFFA6931)
val colorTextAccentYellow = Color(0xFFEFA210)
val colorTextAccentGreen = Color(0xFF2DAE3B)
// color.bg
val colorBgPrimary = Color(0xFF0F0F0F)
val colorBgSecondary = Color(0xFF1B1B1B)
val colorBgTertiary = Color(0xFF2C2C2C)
val colorBgBrand = Color(0xFF0090F9)
val colorBgInverse = Color(0xFFF4F4F4)
val colorBgBase = Color(0xFF000000)
val colorBgOpaquePrimary = Color(0x0DFFFFFF)
val colorBgOpaqueSecondary = Color(0x1AFFFFFF)
val colorBgStatusSuccess = Color(0xFF2DA30D)
val colorBgStatusError = Color(0xFFF25508)
val colorBgStatusWarning = Color(0xFFE68A03)
val colorBgStatusInfo = Color(0xFF0090F9)
val colorBgAccentBlue = Color(0xFF0090F9)
val colorBgAccentViolet = Color(0xFFA967FD)
val colorBgAccentRed = Color(0xFFFE4142)
val colorBgAccentOrange = Color(0xFFF25508)
val colorBgAccentYellow = Color(0xFFE68A03)
val colorBgAccentGreen = Color(0xFF2DA30D)
// color.icon
val colorIconPrimary = Color(0xFFFFFFFF)
val colorIconSecondary = Color(0x99FFFFFF)
val colorIconTertiary = Color(0x4DFFFFFF)
val colorIconBrand = Color(0xFF0090F9)
val colorIconStaticLight = Color(0xFF000000)
val colorIconStaticDark = Color(0xFFFFFFFF)
val colorIconInverse = Color(0xFF0F0F0F)
val colorIconStatusSuccess = Color(0xFF2DAE3B)
val colorIconStatusError = Color(0xFFFA6931)
val colorIconStatusWarning = Color(0xFFEFA210)
val colorIconStatusInfo = Color(0xFF109FF0)
val colorIconAccentBlue = Color(0xFF109FF0)
val colorIconAccentViolet = Color(0xFFB07BFD)
val colorIconAccentRed = Color(0xFFFF5E66)
val colorIconAccentOrange = Color(0xFFFA6931)
val colorIconAccentYellow = Color(0xFFEFA210)
val colorIconAccentGreen = Color(0xFF2DAE3B)
// color.border
val colorBorderPrimary = Color(0x0DFFFFFF)
val colorBorderSecondary = Color(0x1AFFFFFF)
val colorBorderTertiary = Color(0x33FFFFFF)
val colorBorderBrand = Color(0xFF0090F9)
val colorBorderInversePrimary = Color(0x0D000000)
val colorBorderInverseSecondary = Color(0x1A000000)
val colorBorderInverseTertiary = Color(0x33000000)
val colorBorderStatusSuccess = Color(0xFF2DAE3B)
val colorBorderStatusError = Color(0xFFFA6931)
val colorBorderStatusWarning = Color(0xFFEFA210)
val colorBorderStatusInfo = Color(0xFF109FF0)
val colorBorderAccentBlue = Color(0xFF109FF0)
val colorBorderAccentViolet = Color(0xFFB07BFD)
val colorBorderAccentRed = Color(0xFFFF5E66)
val colorBorderAccentOrange = Color(0xFFFA6931)
val colorBorderAccentYellow = Color(0xFFEFA210)
val colorBorderAccentGreen = Color(0xFF2DAE3B)
// color.overlay
val colorOverlayModal = Color(0xCC000000)
// color.interaction
val colorInteractionPress = Color(0x1AFFFFFF)
val colorInteractionPressStaticLight = Color(0x1A000000)
val colorInteractionPressStaticDark = Color(0x1AFFFFFF)
val colorInteractionPressInverse = Color(0x1A000000)
// color.material
val colorMaterialTintGlass = Color(0x66181818)
val colorMaterialTintBlur = Color(0x00000000)
val colorMaterialTintSolid = Color(0x1AFFFFFF)
val colorMaterialFillGlass = Color(0x00000000)
val colorMaterialFillBlur = Color(0x1AFFFFFF)
val colorMaterialFillSolid = Color(0xE62C2C2C)
val colorMaterialLightenGlass = Color(0x33181818)
val colorMaterialLightenBlur = Color(0x00000000)
val colorMaterialLightenSolid = Color(0x00000000)
val colorMaterialSoftLightGlass = Color(0x1A000000)
val colorMaterialSoftLightBlur = Color(0x00000000)
val colorMaterialSoftLightSolid = Color(0x00000000)
val colorMaterialBorderStart = Color(0x33FFFFFF)
val colorMaterialBorderMid = Color(0x00FFFFFF)
val colorMaterialBorderEnd = Color(0x1AFFFFFF)
}

View file

@ -0,0 +1,108 @@
@file:Suppress("all")
package com.tangem.core.ui.res.generated
import androidx.compose.runtime.Stable
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
/**
* Auto-generated from design tokens. Do not edit manually.
*/
@Stable
class TangemDimens3 internal constructor(
val opacity: Opacity = Opacity(),
val blur: Blur = Blur(),
val borderRadius: BorderRadius = BorderRadius(),
val borderWidth: BorderWidth = BorderWidth(),
val size: Size = Size(),
val spacing: Spacing = Spacing(),
) {
@Stable
class Opacity internal constructor(
val disabled: Float = 0.4f,
)
@Stable
class Blur internal constructor(
val card: Dp = 48.dp,
val Button: Dp = 32.dp,
)
@Stable
class BorderRadius internal constructor(
val b100: Dp = 8.dp,
val b150: Dp = 12.dp,
val b200: Dp = 16.dp,
val b250: Dp = 20.dp,
val b300: Dp = 24.dp,
val b400: Dp = 32.dp,
val none: Dp = 0.dp,
val b075: Dp = 6.dp,
val b050: Dp = 4.dp,
val full: Dp = 999.dp,
)
@Stable
class BorderWidth internal constructor(
val none: Dp = 0.dp,
val xs: Dp = 0.5.dp,
val sm: Dp = 1.dp,
val md: Dp = 2.dp,
val lg: Dp = 4.dp,
)
@Stable
class Size internal constructor(
val s100: Dp = 8.dp,
val s125: Dp = 10.dp,
val s150: Dp = 12.dp,
val s200: Dp = 16.dp,
val s250: Dp = 20.dp,
val s300: Dp = 24.dp,
val s350: Dp = 28.dp,
val s400: Dp = 32.dp,
val s450: Dp = 36.dp,
val s500: Dp = 40.dp,
val s550: Dp = 44.dp,
val s600: Dp = 48.dp,
val s700: Dp = 56.dp,
val s800: Dp = 64.dp,
val s900: Dp = 72.dp,
val s1000: Dp = 80.dp,
val s1100: Dp = 88.dp,
val s1200: Dp = 96.dp,
val s025: Dp = 2.dp,
val s050: Dp = 4.dp,
val card: Card = Card(),
) {
@Stable
class Card internal constructor(
val sm: Dp = 128.dp,
)
}
@Stable
class Spacing internal constructor(
val s100: Dp = 8.dp,
val s125: Dp = 10.dp,
val s150: Dp = 12.dp,
val s200: Dp = 16.dp,
val s250: Dp = 20.dp,
val s300: Dp = 24.dp,
val s350: Dp = 28.dp,
val s400: Dp = 32.dp,
val s450: Dp = 36.dp,
val s500: Dp = 40.dp,
val s550: Dp = 44.dp,
val s600: Dp = 48.dp,
val s700: Dp = 56.dp,
val s800: Dp = 64.dp,
val s900: Dp = 72.dp,
val s1000: Dp = 80.dp,
val s025: Dp = 2.dp,
val s050: Dp = 4.dp,
val s075: Dp = 6.dp,
val none: Dp = 0.dp,
)
}

View file

@ -1,72 +0,0 @@
package com.tangem.core.ui.res.generated
import androidx.compose.ui.unit.dp
/**
* Auto-generated from design tokens. Do not edit manually.
*/
internal class TangemDimensionTokens {
// blur
val blurSizeCard = 48.dp
val blurSizeButton = 32.dp
// border-radius
val borderRadius100 = 8.dp
val borderRadius150 = 12.dp
val borderRadius200 = 16.dp
val borderRadius250 = 20.dp
val borderRadius300 = 24.dp
val borderRadius400 = 32.dp
val borderRadiusNone = 0.dp
val borderRadius075 = 6.dp
val borderRadius050 = 4.dp
val borderRadiusFull = 999.dp
// border-width
val borderWidthNone = 0.dp
val borderWidthXs = 0.5.dp
val borderWidthSm = 1.dp
val borderWidthMd = 2.dp
val borderWidthLg = 4.dp
// size
val size100 = 8.dp
val size125 = 10.dp
val size150 = 12.dp
val size200 = 16.dp
val size250 = 20.dp
val size300 = 24.dp
val size350 = 28.dp
val size400 = 32.dp
val size450 = 36.dp
val size500 = 40.dp
val size550 = 44.dp
val size600 = 48.dp
val size700 = 56.dp
val size800 = 64.dp
val size1000 = 80.dp
val size1100 = 88.dp
val size1200 = 96.dp
val size025 = 2.dp
val size050 = 4.dp
val sizeCardSm = 128.dp
// spacing
val spacing100 = 8.dp
val spacing150 = 12.dp
val spacing200 = 16.dp
val spacing250 = 20.dp
val spacing300 = 24.dp
val spacing350 = 28.dp
val spacing400 = 32.dp
val spacing450 = 36.dp
val spacing500 = 40.dp
val spacing550 = 44.dp
val spacing600 = 48.dp
val spacing700 = 56.dp
val spacing800 = 64.dp
val spacing1000 = 80.dp
val spacing025 = 2.dp
val spacing050 = 4.dp
val spacingNone = 0.dp
}

View file

@ -1,118 +0,0 @@
package com.tangem.core.ui.res.generated
import androidx.compose.ui.graphics.Color
/**
* Auto-generated from design tokens. Do not edit manually.
* Theme: Light
*/
internal class TangemLightColorTokens {
// color.text
val colorTextPrimary = Color(0xFF0F0F0F)
val colorTextSecondary = Color(0x990F0F0F)
val colorTextTertiary = Color(0x660F0F0F)
val colorTextBrand = Color(0xFF0090F9)
val colorTextStaticLightPrimary = Color(0xFF000000)
val colorTextStaticLightSecondary = Color(0x99000000)
val colorTextStaticLightTertiary = Color(0x66000000)
val colorTextStaticDarkPrimary = Color(0xFFFFFFFF)
val colorTextStaticDarkSecondary = Color(0x99FFFFFF)
val colorTextStaticDarkTertiary = Color(0x4DFFFFFF)
val colorTextInversePrimary = Color(0xFFFFFFFF)
val colorTextInverseSecondary = Color(0x99FFFFFF)
val colorTextInverseTertiary = Color(0x4DFFFFFF)
val colorTextStatusSuccess = Color(0xFF2DA30D)
val colorTextStatusError = Color(0xFFFE4142)
val colorTextStatusWarning = Color(0xFFE68A03)
val colorTextStatusInfo = Color(0xFF0090F9)
val colorTextAccentBlue = Color(0xFF0090F9)
val colorTextAccentViolet = Color(0xFFA967FD)
val colorTextAccentRed = Color(0xFFFE4142)
val colorTextAccentOrange = Color(0xFFF25508)
val colorTextAccentYellow = Color(0xFFE68A03)
val colorTextAccentGreen = Color(0xFF2DA30D)
// color.bg
val colorBgPrimary = Color(0xFFF4F4F4)
val colorBgSecondary = Color(0xFFFFFFFF)
val colorBgTertiary = Color(0xFFEAEAEA)
val colorBgBrand = Color(0xFF0090F9)
val colorBgInverse = Color(0xFFF4F4F4)
val colorBgBase = Color(0xFFF4F4F4)
val colorBgOpaquePrimary = Color(0x0DFFFFFF)
val colorBgOpaqueSecondary = Color(0x1AFFFFFF)
val colorBgStatusSuccess = Color(0xFF2DA30D)
val colorBgStatusError = Color(0xFFF25508)
val colorBgStatusWarning = Color(0xFFE68A03)
val colorBgStatusInfo = Color(0xFF0090F9)
val colorBgAccentBlue = Color(0xFF0090F9)
val colorBgAccentViolet = Color(0xFFA967FD)
val colorBgAccentRed = Color(0xFFFE4142)
val colorBgAccentOrange = Color(0xFFF25508)
val colorBgAccentYellow = Color(0xFFE68A03)
val colorBgAccentGreen = Color(0xFF2DA30D)
// color.icon
val colorIconPrimary = Color(0xFF0F0F0F)
val colorIconSecondary = Color(0x990F0F0F)
val colorIconTertiary = Color(0x660F0F0F)
val colorIconBrand = Color(0xFF0090F9)
val colorIconStaticLight = Color(0xFF000000)
val colorIconStaticDark = Color(0xFFFFFFFF)
val colorIconInverse = Color(0xFFFFFFFF)
val colorIconStatusSuccess = Color(0xFF2DA30D)
val colorIconStatusError = Color(0xFFF25508)
val colorIconStatusWarning = Color(0xFFE68A03)
val colorIconStatusInfo = Color(0xFF0090F9)
val colorIconAccentBlue = Color(0xFF0090F9)
val colorIconAccentViolet = Color(0xFFA967FD)
val colorIconAccentRed = Color(0xFFFE4142)
val colorIconAccentOrange = Color(0xFFF25508)
val colorIconAccentYellow = Color(0xFFE68A03)
val colorIconAccentGreen = Color(0xFF2DA30D)
// color.border
val colorBorderPrimary = Color(0x0D000000)
val colorBorderSecondary = Color(0x1A000000)
val colorBorderTertiary = Color(0x33000000)
val colorBorderBrand = Color(0xFF0090F9)
val colorBorderInversePrimary = Color(0x0D000000)
val colorBorderInverseSecondary = Color(0x1A000000)
val colorBorderInverseTertiary = Color(0x33000000)
val colorBorderStatusSuccess = Color(0xFF2DA30D)
val colorBorderStatusError = Color(0xFFF25508)
val colorBorderStatusWarning = Color(0xFFE68A03)
val colorBorderStatusInfo = Color(0xFF0090F9)
val colorBorderAccentBlue = Color(0xFF0090F9)
val colorBorderAccentViolet = Color(0xFFA967FD)
val colorBorderAccentRed = Color(0xFFFE4142)
val colorBorderAccentOrange = Color(0xFFF25508)
val colorBorderAccentYellow = Color(0xFFE68A03)
val colorBorderAccentGreen = Color(0xFF2DA30D)
// color.overlay
val colorOverlayModal = Color(0x99000000)
// color.interaction
val colorInteractionPress = Color(0x1A000000)
val colorInteractionPressStaticLight = Color(0x1A000000)
val colorInteractionPressStaticDark = Color(0x1AFFFFFF)
val colorInteractionPressInverse = Color(0x1AFFFFFF)
// color.material
val colorMaterialTintGlass = Color(0x00000000)
val colorMaterialTintBlur = Color(0x00000000)
val colorMaterialTintSolid = Color(0x1AFFFFFF)
val colorMaterialFillGlass = Color(0x00000000)
val colorMaterialFillBlur = Color(0x99FFFFFF)
val colorMaterialFillSolid = Color(0xE6FFFFFF)
val colorMaterialLightenGlass = Color(0x80F7F7F7)
val colorMaterialLightenBlur = Color(0x00000000)
val colorMaterialLightenSolid = Color(0x00000000)
val colorMaterialSoftLightGlass = Color(0x33000000)
val colorMaterialSoftLightBlur = Color(0x00000000)
val colorMaterialSoftLightSolid = Color(0x00000000)
val colorMaterialBorderStart = Color(0x26000000)
val colorMaterialBorderMid = Color(0x00000000)
val colorMaterialBorderEnd = Color(0x1A000000)
}

View file

@ -1,22 +0,0 @@
package com.tangem.core.ui.res.generated
/**
* Auto-generated from design tokens. Do not edit manually.
*/
internal class TangemOpacityTokens {
val opacity0 = 0f
val opacity5 = 0.05f
val opacity10 = 0.1f
val opacity15 = 0.15f
val opacity20 = 0.2f
val opacity25 = 0.25f
val opacity30 = 0.3f
val opacity40 = 0.4f
val opacity50 = 0.5f
val opacity60 = 0.6f
val opacity70 = 0.7f
val opacity80 = 0.8f
val opacity90 = 0.9f
val opacity100 = 1f
val opacityDisabled = 0.4f
}

View file

@ -1,15 +0,0 @@
package com.tangem.core.ui.res.generated
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
/**
* Auto-generated from design tokens. Do not edit manually.
*/
internal class TangemShadowTokens {
val shadowButtonBlur = 40.dp
val shadowButtonOffsetX = 0.dp
val shadowButtonOffsetY = 8.dp
val shadowButtonSpread = 0.dp
val shadowButtonColor = Color(0x1A000000)
}

View file

@ -0,0 +1,112 @@
@file:Suppress("all")
package com.tangem.core.ui.res.generated
import androidx.compose.runtime.Stable
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.LineBreak
import androidx.compose.ui.text.style.LineHeightStyle
import androidx.compose.ui.unit.sp
/**
* Auto-generated from design tokens. Do not edit manually.
*/
@Stable
class TangemTypography3 internal constructor(fontFamily: FontFamily) {
val display: Display = Display(fontFamily)
val heading: Heading = Heading(fontFamily)
val body: Body = Body(fontFamily)
val subheading: Subheading = Subheading(fontFamily)
val caption: Caption = Caption(fontFamily)
@Stable
class Display internal constructor(fontFamily: FontFamily) {
val medium: TextStyle = TextStyle(
fontFamily = fontFamily,
fontWeight = FontWeight.SemiBold,
fontSize = 44.sp,
lineHeight = 52.sp,
letterSpacing = (-0.92).sp,
lineHeightStyle = LineHeightStyle(
alignment = LineHeightStyle.Alignment.Center,
trim = LineHeightStyle.Trim.None,
),
lineBreak = LineBreak.Heading,
)
}
@Stable
class Heading internal constructor(fontFamily: FontFamily) {
val medium: TextStyle = TextStyle(
fontFamily = fontFamily,
fontWeight = FontWeight.SemiBold,
fontSize = 28.sp,
lineHeight = 33.sp,
letterSpacing = (-0.37).sp,
lineHeightStyle = LineHeightStyle(
alignment = LineHeightStyle.Alignment.Center,
trim = LineHeightStyle.Trim.None,
),
lineBreak = LineBreak.Heading,
)
val small: TextStyle = TextStyle(
fontFamily = fontFamily,
fontWeight = FontWeight.SemiBold,
fontSize = 20.sp,
lineHeight = 24.sp,
letterSpacing = (-0.12).sp,
lineHeightStyle = LineHeightStyle(
alignment = LineHeightStyle.Alignment.Center,
trim = LineHeightStyle.Trim.None,
),
lineBreak = LineBreak.Heading,
)
}
@Stable
class Body internal constructor(fontFamily: FontFamily) {
val medium: TextStyle = TextStyle(
fontFamily = fontFamily,
fontWeight = FontWeight.Medium,
fontSize = 16.sp,
lineHeight = 20.sp,
letterSpacing = 0.02.sp,
lineHeightStyle = LineHeightStyle(
alignment = LineHeightStyle.Alignment.Center,
trim = LineHeightStyle.Trim.None,
),
)
}
@Stable
class Subheading internal constructor(fontFamily: FontFamily) {
val medium: TextStyle = TextStyle(
fontFamily = fontFamily,
fontWeight = FontWeight.Medium,
fontSize = 14.sp,
lineHeight = 17.sp,
letterSpacing = 0.07.sp,
lineHeightStyle = LineHeightStyle(
alignment = LineHeightStyle.Alignment.Center,
trim = LineHeightStyle.Trim.None,
),
)
}
@Stable
class Caption internal constructor(fontFamily: FontFamily) {
val medium: TextStyle = TextStyle(
fontFamily = fontFamily,
fontWeight = FontWeight.Medium,
fontSize = 12.sp,
lineHeight = 16.sp,
letterSpacing = 0.18.sp,
lineHeightStyle = LineHeightStyle(
alignment = LineHeightStyle.Alignment.Center,
trim = LineHeightStyle.Trim.None,
),
)
}
}

View file

@ -1,88 +0,0 @@
package com.tangem.core.ui.res.generated
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.LineBreak
import androidx.compose.ui.text.style.LineHeightStyle
import androidx.compose.ui.unit.sp
import com.tangem.core.ui.res.InterFamily
/**
* Auto-generated from design tokens. Do not edit manually.
*/
internal class TangemTypographyTokens {
val fontDisplayMedium = TextStyle(
fontFamily = InterFamily,
fontWeight = FontWeight.SemiBold,
fontSize = 44.sp,
lineHeight = 52.sp,
letterSpacing = (-0.92).sp,
lineHeightStyle = LineHeightStyle(
alignment = LineHeightStyle.Alignment.Center,
trim = LineHeightStyle.Trim.None,
),
lineBreak = LineBreak.Heading,
)
val fontHeadingMedium = TextStyle(
fontFamily = InterFamily,
fontWeight = FontWeight.SemiBold,
fontSize = 28.sp,
lineHeight = 33.sp,
letterSpacing = (-0.37).sp,
lineHeightStyle = LineHeightStyle(
alignment = LineHeightStyle.Alignment.Center,
trim = LineHeightStyle.Trim.None,
),
lineBreak = LineBreak.Heading,
)
val fontHeadingSmall = TextStyle(
fontFamily = InterFamily,
fontWeight = FontWeight.SemiBold,
fontSize = 20.sp,
lineHeight = 24.sp,
letterSpacing = (-0.12).sp,
lineHeightStyle = LineHeightStyle(
alignment = LineHeightStyle.Alignment.Center,
trim = LineHeightStyle.Trim.None,
),
lineBreak = LineBreak.Heading,
)
val fontBodyMedium = TextStyle(
fontFamily = InterFamily,
fontWeight = FontWeight.Medium,
fontSize = 16.sp,
lineHeight = 20.sp,
letterSpacing = 0.02.sp,
lineHeightStyle = LineHeightStyle(
alignment = LineHeightStyle.Alignment.Center,
trim = LineHeightStyle.Trim.None,
),
)
val fontSubheadingMedium = TextStyle(
fontFamily = InterFamily,
fontWeight = FontWeight.Medium,
fontSize = 14.sp,
lineHeight = 17.sp,
letterSpacing = 0.07.sp,
lineHeightStyle = LineHeightStyle(
alignment = LineHeightStyle.Alignment.Center,
trim = LineHeightStyle.Trim.None,
),
)
val fontCaptionMedium = TextStyle(
fontFamily = InterFamily,
fontWeight = FontWeight.Medium,
fontSize = 12.sp,
lineHeight = 16.sp,
letterSpacing = 0.18.sp,
lineHeightStyle = LineHeightStyle(
alignment = LineHeightStyle.Alignment.Center,
trim = LineHeightStyle.Trim.None,
),
)
}

View file

@ -0,0 +1,21 @@
# token-gen
Generates Kotlin (Jetpack Compose) source files from design tokens defined in the `ds-tokens` git submodule.
## Updating tokens
1. Update the `ds-tokens` submodule to the latest commit:
```bash
git submodule update --remote core/ui/ds-tokens
```
2. Re-run the build:
```bash
cd core/ui/token-gen && npm run build
```
3. Commit both the submodule pointer and generated files.
## How it works
The script uses [Style Dictionary v5](https://styledictionary.com/) with [@tokens-studio/sd-transforms](https://github.com/tokens-studio/sd-transforms) to read JSON token files from `core/ui/ds-tokens/tokens/` and generate Kotlin files into `core/ui/src/main/java/com/tangem/core/ui/res/generated/`.
All generated files are written to `com.tangem.core.ui.res.generated` and should not be edited manually.

View file

@ -149,47 +149,368 @@ function groupByPath(tokens, depth = 1) {
return groups;
}
/**
* Build a tree of nested objects from entries.
* Each entry: { path: string[], value: string }.
* The last segment is the property name; preceding segments become nested objects.
*/
function buildPropertyTree(entries) {
const root = { props: [], children: new Map() };
for (const { path, value } of entries) {
let node = root;
for (let i = 0; i < path.length - 1; i++) {
const seg = path[i];
if (!node.children.has(seg)) {
node.children.set(seg, { props: [], children: new Map() });
}
node = node.children.get(seg);
}
const propName = path[path.length - 1];
const existingIdx = node.props.findIndex(p => p.name === propName);
if (existingIdx >= 0) {
console.warn(` ⚠ Duplicate property path: ${path.join('.')} — overwriting`);
node.props[existingIdx] = { name: propName, value };
} else {
node.props.push({ name: propName, value });
}
}
return root;
}
/**
* Render a property tree as Kotlin nested objects.
* Returns an array of indented lines.
*/
function renderTree(node, indent = 1) {
const pad = ' '.repeat(indent);
const lines = [];
for (const { name, value } of node.props) {
lines.push(`${pad}val ${name} = ${value}`);
}
for (const [name, child] of node.children) {
if (lines.length > 0) lines.push('');
lines.push(`${pad}object ${name} {`);
lines.push(...renderTree(child, indent + 1));
lines.push(`${pad}}`);
}
return lines;
}
/**
* Render a @Stable class tree for dimension tokens.
* Each node with children becomes a nested @Stable class.
* Props carry { default, type } values.
*/
function renderStableDimenClass(className, node, indent) {
const pad = ' '.repeat(indent);
const pad1 = ' '.repeat(indent + 1);
const lines = [];
lines.push(`${pad}@Stable`);
lines.push(`${pad}class ${className} internal constructor(`);
for (const { name, value } of node.props) {
lines.push(`${pad1}val ${kotlinSafe(name)}: ${value.type} = ${value.default},`);
}
for (const [childName, childNode] of node.children) {
const typeName = capitalize(childName);
const propName = kotlinSafe(childName.charAt(0).toLowerCase() + childName.slice(1));
lines.push(`${pad1}val ${propName}: ${typeName} = ${typeName}(),`);
}
if (node.children.size === 0) {
lines.push(`${pad})`);
} else {
lines.push(`${pad}) {`);
let first = true;
for (const [childName, childNode] of node.children) {
if (!first) lines.push('');
first = false;
lines.push(...renderStableDimenClass(capitalize(childName), childNode, indent + 1));
}
lines.push(`${pad}}`);
}
return lines;
}
/**
* Capitalize the first letter of a string.
*/
function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
/**
* Convert a kebab-case segment to PascalCase (for object names).
*/
function toPascalCase(seg) {
return seg
.split('-')
.map(part => capitalize(part))
.join('');
}
/**
* Wrap a name in backticks if it starts with a digit or is a Kotlin hard keyword.
*/
const KOTLIN_HARD_KEYWORDS = new Set([
'as', 'break', 'class', 'continue', 'do', 'else', 'false', 'for', 'fun',
'if', 'in', 'interface', 'is', 'null', 'object', 'package', 'return',
'super', 'this', 'throw', 'true', 'try', 'typealias', 'typeof', 'val',
'var', 'when', 'while',
]);
function kotlinSafe(name) {
if (/^\d/.test(name) || KOTLIN_HARD_KEYWORDS.has(name)) return `\`${name}\``;
return name;
}
// ── TangemColors3 helpers ─────────────────────────────────────────────────────
/** Shared structure tree for TangemColors3, computed from light theme codeSyntax. */
let colors3StructureTree = null;
/** Extract codeSyntax.Android path, stripping TangemTheme.colors3. prefix. */
function getAndroidCodeSyntax(token) {
const ext = token.$extensions?.['com.figma.codeSyntax'];
if (!ext?.Android) return null;
const prefix = 'TangemTheme.colors3.';
const android = ext.Android;
return android.startsWith(prefix) ? android.slice(prefix.length) : null;
}
/** Get the token's property path for the class tree from codeSyntax, or fallback to JSON path. */
function colorTokenClassPath(token) {
const cs = getAndroidCodeSyntax(token);
if (cs) return cs.split('.');
// Fallback for material tokens (no codeSyntax): use JSON path minus 'color' prefix
const pathSegs = token.path.slice(1); // remove 'color'
return pathSegs.map(seg => seg.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase()));
}
/**
* Extract a simple palette reference from a token's original (unresolved) value.
* Returns the reference string like "palette.neutral.95" or null for complex values.
*/
function extractPaletteRef(token) {
const original = token.original?.$value;
if (typeof original !== 'string') return null;
const match = original.match(/^\{(palette\.[^}]+)\}$/);
return match ? match[1] : null;
}
/**
* Convert a palette reference path to Kotlin code referencing TangemColorPalette.
* e.g., "palette.neutral.95" "TangemColorPalette.Neutral.`95`"
* e.g., "palette.opaque.base-black.60" "TangemColorPalette.Opaque.BaseBlack.`60`"
*/
function paletteRefToKotlin(refPath) {
const parts = refPath.split('.').slice(1); // drop "palette"
const objParts = parts.slice(0, -1).map(seg => toPascalCase(seg));
const leaf = parts[parts.length - 1].replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
return `TangemColorPalette.${objParts.join('.')}.${kotlinSafe(leaf)}`;
}
/** Get Kotlin value for a color token: palette reference if simple, else resolved Color literal. */
function paletteRefOrColor(token) {
const ref = extractPaletteRef(token);
if (ref) return paletteRefToKotlin(ref);
return toComposeColor(token.$value, token.path.join('.'));
}
/**
* Build a class structure tree from color tokens using codeSyntax paths.
* Each node: { props: [{name, jsonPath}], children: Map<string, node> }
*/
function buildClassStructureTree(tokens) {
const root = { props: [], children: new Map() };
for (const token of tokens) {
const classPath = colorTokenClassPath(token);
const jsonPath = token.path.join('.');
let node = root;
for (let i = 0; i < classPath.length - 1; i++) {
const seg = classPath[i];
if (!node.children.has(seg)) {
node.children.set(seg, { props: [], children: new Map() });
}
node = node.children.get(seg);
}
const propName = classPath[classPath.length - 1];
const existingIdx = node.props.findIndex(p => p.name === propName);
if (existingIdx >= 0) {
console.warn(` ⚠ Duplicate codeSyntax path: ${classPath.join('.')} (${jsonPath}) — overwriting`);
node.props[existingIdx] = { name: propName, jsonPath };
} else {
node.props.push({ name: propName, jsonPath });
}
}
return root;
}
/**
* Resolve conflicts where a name appears as both a leaf property and a child class.
* Resolution: move the leaf into the child as "default".
*/
function resolveClassTreeConflicts(node) {
const propsToRemove = [];
for (const [childName, childNode] of node.children) {
const conflictIdx = node.props.findIndex(p => p.name === childName);
if (conflictIdx >= 0) {
const prop = node.props[conflictIdx];
console.log(` Conflict resolved: "${childName}" is both property and class → moved to "${childName}.default"`);
childNode.props.unshift({ name: 'default', jsonPath: prop.jsonPath });
propsToRemove.push(conflictIdx);
}
}
for (const idx of propsToRemove.sort((a, b) => b - a)) {
node.props.splice(idx, 1);
}
for (const child of node.children.values()) {
resolveClassTreeConflicts(child);
}
}
/**
* Render a @Stable class with mutableStateOf pattern for Compose theme colors.
* Returns array of Kotlin source lines.
*/
function renderStableClass(className, node, indent = 0) {
const pad = ' '.repeat(indent);
const pad1 = ' '.repeat(indent + 1);
const pad2 = ' '.repeat(indent + 2);
const lines = [];
lines.push(`${pad}@Stable`);
lines.push(`${pad}class ${className} internal constructor(`);
for (const { name } of node.props) {
lines.push(`${pad1}${kotlinSafe(name)}: Color,`);
}
for (const [childName] of node.children) {
lines.push(`${pad1}val ${childName}: ${capitalize(childName)},`);
}
lines.push(`${pad}) {`);
// mutableStateOf delegates for leaf Color props
if (node.props.length > 0) {
for (const { name } of node.props) {
const safe = kotlinSafe(name);
lines.push(`${pad1}var ${safe} by mutableStateOf(${safe})`);
lines.push(`${pad2}private set`);
}
}
// Nested child classes
for (const [childName, childNode] of node.children) {
lines.push('');
lines.push(...renderStableClass(capitalize(childName), childNode, indent + 1));
}
// update() function
lines.push('');
lines.push(`${pad1}fun update(other: ${className}) {`);
for (const { name } of node.props) {
const safe = kotlinSafe(name);
lines.push(`${pad2}${safe} = other.${safe}`);
}
for (const [childName] of node.children) {
lines.push(`${pad2}${childName}.update(other.${childName})`);
}
lines.push(`${pad1}}`);
lines.push(`${pad}}`);
return lines;
}
/**
* Render the content lines of a factory constructor call (param assignments + child constructors).
*/
function renderFactoryContent(classPath, node, valueMap, indent) {
const pad = ' '.repeat(indent);
const lines = [];
for (const { name, jsonPath } of node.props) {
const value = valueMap.get(jsonPath);
if (!value) console.warn(` ⚠ No value for jsonPath "${jsonPath}" (property: ${name})`);
lines.push(`${pad}${kotlinSafe(name)} = ${value || 'Color.Unspecified'},`);
}
for (const [childName, childNode] of node.children) {
const childClassPath = `${classPath}.${capitalize(childName)}`;
lines.push(`${pad}${childName} = ${childClassPath}(`);
lines.push(...renderFactoryContent(childClassPath, childNode, valueMap, indent + 1));
lines.push(`${pad}),`);
}
return lines;
}
const FILE_SUPPRESS = '@file:Suppress("all")';
// ── Custom formats ─────────────────────────────────────────────────────────────
/**
* Kotlin format for color tokens.
* Generates: class TangemLightColorTokens / TangemDarkColorTokens
* Kotlin format for palette tokens.
* Generates nested objects: object Base { val black = ... }, object Neutral { val `5` = ... }, etc.
*/
StyleDictionary.registerFormat({
name: 'kotlin/compose-colors',
format: ({ dictionary, options }) => {
const themeName = options.themeName; // "Light" or "Dark"
const objectName = `Tangem${themeName}ColorTokens`;
const colorTokens = dictionary.allTokens.filter(
t => t.$type === 'color' && !isSourceOnlyToken(t),
name: 'kotlin/compose-palette',
format: ({ dictionary }) => {
const paletteTokens = dictionary.allTokens.filter(
t => t.$type === 'color' && t.path[0] === 'palette',
);
// Group by top-level category (color.text, color.bg, color.icon, etc.)
const groups = groupByPath(colorTokens, 2);
const sections = [];
for (const [groupKey, tokens] of Object.entries(groups)) {
const comment = ` // ${groupKey}`;
const props = tokens.map(token => {
const propName = toCamelCase(token.path);
const value = toComposeColor(token.$value, token.path.join('.'));
return ` val ${propName} = ${value}`;
});
sections.push([comment, ...props].join('\n'));
const entries = paletteTokens.map(token => {
// palette.base.black → ["Base", "black"]
// palette.neutral.5 → ["Neutral", "`5`"]
// palette.opaque.base-black.5 → ["Opaque", "BaseBlack", "`5`"]
const segments = token.path.slice(1); // drop "palette"
const path = segments.map((seg, i) => {
if (i < segments.length - 1) {
// intermediate segments → PascalCase object names
return toPascalCase(seg);
}
// leaf segment → property name, backtick-wrap if starts with digit
const camel = seg.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
return kotlinSafe(camel);
});
const value = toComposeColor(token.$value, token.path.join('.'));
return { path, value };
});
const tree = buildPropertyTree(entries);
const body = renderTree(tree);
return [
FILE_SUPPRESS,
'',
`package ${PACKAGE}`,
'',
'import androidx.compose.ui.graphics.Color',
'',
'/**',
` * Auto-generated from design tokens. Do not edit manually.`,
` * Theme: ${themeName}`,
' * Auto-generated from design tokens. Do not edit manually.',
' */',
`internal class ${objectName} {`,
sections.join('\n\n'),
'internal object TangemColorPalette {',
body.join('\n'),
'}',
'',
].join('\n');
@ -197,100 +518,98 @@ StyleDictionary.registerFormat({
});
/**
* Kotlin format for dimension tokens (spacing, size, border-radius, border-width).
* Kotlin format for TangemDimens3 structured dimension tokens as @Immutable data class.
* Generates nested @Immutable data classes from codeSyntax.Android paths (prefix: TangemTheme.dimens3.).
* Includes spacing, size, borderRadius, borderWidth, blur, and semantic opacity tokens.
*/
StyleDictionary.registerFormat({
name: 'kotlin/compose-dimensions',
name: 'kotlin/compose-dimens3',
format: ({ dictionary }) => {
const dimTokens = dictionary.allTokens.filter(t => {
// Exclude font tokens — letter-spacing values are already in typography tokens as sp
if (t.path[0] === 'font') return false;
return (
(t.$type === 'dimension' || t.$type === 'borderRadius' || t.$type === 'borderWidth') &&
!isSourceOnlyToken(t)
);
});
const prefix = 'TangemTheme.dimens3.';
const groups = groupByPath(dimTokens, 1);
const sections = [];
const entries = [];
for (const token of dictionary.allTokens) {
const ext = token.$extensions?.['com.figma.codeSyntax'];
if (!ext?.Android?.startsWith(prefix)) continue;
const codePath = ext.Android.slice(prefix.length);
const segPath = codePath.split('.');
for (const [groupKey, tokens] of Object.entries(groups)) {
const comment = ` // ${groupKey}`;
const props = tokens.map(token => {
const propName = toCamelCase(token.path);
// Resolved value is in px (number), convert to dp
const raw = parseFloat(token.$value);
if (isNaN(raw)) throw new Error(`Non-numeric dimension value for ${token.path.join('.')}: "${token.$value}"`);
const dpVal = `${raw}.dp`;
return ` val ${propName} = ${dpVal}`;
});
sections.push([comment, ...props].join('\n'));
const tp = token.path.join('.');
if (isNaN(raw)) throw new Error(`Non-numeric value for ${tp}: "${token.$value}"`);
// Opacity tokens → Float, all others → Dp
const isOpacity = token.$type === 'opacity';
const value = isOpacity ? `${raw}f` : `${raw}.dp`;
const type = isOpacity ? 'Float' : 'Dp';
entries.push({ path: segPath, value, type });
}
const tree = buildPropertyTree(entries.map(e => ({
path: e.path,
value: { default: e.value, type: e.type },
})));
const classLines = renderStableDimenClass('TangemDimens3', tree, 0);
return [
FILE_SUPPRESS,
'',
`package ${PACKAGE}`,
'',
'import androidx.compose.runtime.Stable',
'import androidx.compose.ui.unit.Dp',
'import androidx.compose.ui.unit.dp',
'',
'/**',
' * Auto-generated from design tokens. Do not edit manually.',
' */',
'internal class TangemDimensionTokens {',
sections.join('\n\n'),
'}',
...classLines,
'',
].join('\n');
},
});
/**
* Kotlin format for opacity tokens.
* Kotlin format for TangemTypography3 @Stable class with nested categories.
* Generates a class taking FontFamily, with nested classes for each typography category
* (display, heading, body, subheading, caption).
*/
StyleDictionary.registerFormat({
name: 'kotlin/compose-opacity',
format: ({ dictionary }) => {
const opacityTokens = dictionary.allTokens.filter(
t => t.$type === 'opacity' && !isSourceOnlyToken(t),
);
const props = opacityTokens.map(token => {
const propName = toCamelCase(token.path);
const raw = parseFloat(token.$value);
if (isNaN(raw)) throw new Error(`Non-numeric opacity value for ${token.path.join('.')}: "${token.$value}"`);
const floatVal = `${raw}f`;
return ` val ${propName} = ${floatVal}`;
});
return [
`package ${PACKAGE}`,
'',
'/**',
' * Auto-generated from design tokens. Do not edit manually.',
' */',
'internal class TangemOpacityTokens {',
...props,
'}',
'',
].join('\n');
},
});
/**
* Kotlin format for typography tokens.
*/
StyleDictionary.registerFormat({
name: 'kotlin/compose-typography',
name: 'kotlin/compose-typography3',
format: ({ dictionary }) => {
const typoTokens = dictionary.allTokens.filter(
t => t.$type === 'typography' && !isSourceOnlyToken(t),
);
const props = typoTokens.map(token => {
const propName = toCamelCase(token.path);
const v = token.$value;
// Group by category (path[1]: display, heading, body, subheading, caption)
const categories = new Map();
for (const token of typoTokens) {
const category = token.path[1];
if (!categories.has(category)) categories.set(category, []);
categories.get(category).push(token);
}
// v is an object: { fontFamily, fontWeight, fontSize, lineHeight, letterSpacing, ... }
// Build nested class lines
const outerProps = [];
const innerClasses = [];
for (const [category, tokens] of categories) {
const className = capitalize(category);
const propName = category;
const isHeading = category === 'display' || category === 'heading';
outerProps.push(` val ${propName}: ${className} = ${className}(fontFamily)`);
const classLines = [` @Stable`, ` class ${className} internal constructor(fontFamily: FontFamily) {`];
for (const token of tokens) {
const size = token.path[2]; // medium, small, etc.
const v = token.$value;
const tp = token.path.join('.');
if (!v.fontWeight) throw new Error(`Missing fontWeight for ${tp}`);
const fontWeight = mapFontWeight(v.fontWeight);
const fontSize = parseFloat(v.fontSize);
@ -300,45 +619,49 @@ StyleDictionary.registerFormat({
const letterSpacing = parseFloat(v.letterSpacing);
if (isNaN(letterSpacing)) throw new Error(`Non-numeric letterSpacing for ${tp}: "${v.letterSpacing}"`);
// display and heading categories get LineBreak.Heading (matches TangemTypography2)
const category = token.path[1]; // display, heading, body, subheading, caption
const isHeading = category === 'display' || category === 'heading';
const spacingLiteral = letterSpacing < 0 ? `(${letterSpacing})` : `${letterSpacing}`;
const lines = [
` val ${propName} = TextStyle(`,
` fontFamily = InterFamily,`,
` fontWeight = ${fontWeight},`,
` fontSize = ${fontSize}.sp,`,
` lineHeight = ${lineHeight}.sp,`,
` letterSpacing = ${letterSpacing < 0 ? `(${letterSpacing})` : letterSpacing}.sp,`,
` lineHeightStyle = LineHeightStyle(`,
` alignment = LineHeightStyle.Alignment.Center,`,
` trim = LineHeightStyle.Trim.None,`,
` ),`,
];
classLines.push(` val ${size}: TextStyle = TextStyle(`);
classLines.push(` fontFamily = fontFamily,`);
classLines.push(` fontWeight = ${fontWeight},`);
classLines.push(` fontSize = ${fontSize}.sp,`);
classLines.push(` lineHeight = ${lineHeight}.sp,`);
classLines.push(` letterSpacing = ${spacingLiteral}.sp,`);
classLines.push(` lineHeightStyle = LineHeightStyle(`);
classLines.push(` alignment = LineHeightStyle.Alignment.Center,`);
classLines.push(` trim = LineHeightStyle.Trim.None,`);
classLines.push(` ),`);
if (isHeading) {
lines.push(` lineBreak = LineBreak.Heading,`);
classLines.push(` lineBreak = LineBreak.Heading,`);
}
classLines.push(` )`);
}
lines.push(` )`);
return lines.join('\n');
});
classLines.push(` }`);
innerClasses.push(classLines.join('\n'));
}
return [
FILE_SUPPRESS,
'',
`package ${PACKAGE}`,
'',
'import androidx.compose.runtime.Stable',
'import androidx.compose.ui.text.TextStyle',
'import androidx.compose.ui.text.font.FontFamily',
'import androidx.compose.ui.text.font.FontWeight',
'import androidx.compose.ui.text.style.LineBreak',
'import androidx.compose.ui.text.style.LineHeightStyle',
'import androidx.compose.ui.unit.sp',
'import com.tangem.core.ui.res.InterFamily',
'',
'/**',
' * Auto-generated from design tokens. Do not edit manually.',
' */',
'internal class TangemTypographyTokens {',
props.join('\n\n'),
'@Stable',
'class TangemTypography3 internal constructor(fontFamily: FontFamily) {',
outerProps.join('\n'),
'',
innerClasses.join('\n\n'),
'}',
'',
].join('\n');
@ -361,54 +684,88 @@ function mapFontWeight(value) {
}
/**
* Kotlin format for shadow tokens.
* Shadow tokens are composite (type: shadow) with object $value containing
* blur, spread, color, offsetX, offsetY, type.
* Kotlin format for TangemColors3 class definition.
* Generates the @Stable class hierarchy with mutableStateOf + update() pattern.
* Structure is derived from light theme codeSyntax.Android fields.
*/
StyleDictionary.registerFormat({
name: 'kotlin/compose-shadows',
name: 'kotlin/compose-colors3-class',
format: ({ dictionary }) => {
const shadowTokens = dictionary.allTokens.filter(
t => (t.$type === 'shadow' || t.$type === 'boxShadow') && !isSourceOnlyToken(t),
const colorTokens = dictionary.allTokens.filter(
t => t.$type === 'color' && !isSourceOnlyToken(t),
);
const entries = shadowTokens.map(token => {
const propName = toCamelCase(token.path);
const v = token.$value;
const sp = token.path.join('.');
if (!v) throw new Error(`Missing shadow value for ${sp}`);
const blur = parseFloat(v.blur);
if (isNaN(blur)) throw new Error(`Non-numeric blur for ${sp}: "${v.blur}"`);
const spread = parseFloat(v.spread);
if (isNaN(spread)) throw new Error(`Non-numeric spread for ${sp}: "${v.spread}"`);
const offsetX = parseFloat(v.offsetX);
if (isNaN(offsetX)) throw new Error(`Non-numeric offsetX for ${sp}: "${v.offsetX}"`);
const offsetY = parseFloat(v.offsetY);
if (isNaN(offsetY)) throw new Error(`Non-numeric offsetY for ${sp}: "${v.offsetY}"`);
if (!v.color) throw new Error(`Missing color for ${sp}`);
const colorVal = toComposeColor(v.color, sp + '.color');
return [
` val ${propName}Blur = ${blur}.dp`,
` val ${propName}OffsetX = ${offsetX}.dp`,
` val ${propName}OffsetY = ${offsetY}.dp`,
` val ${propName}Spread = ${spread}.dp`,
` val ${propName}Color = ${colorVal}`,
].join('\n');
});
colors3StructureTree = buildClassStructureTree(colorTokens);
resolveClassTreeConflicts(colors3StructureTree);
const classLines = renderStableClass('TangemColors3', colors3StructureTree, 0);
return [
FILE_SUPPRESS,
'',
`package ${PACKAGE}`,
'',
'import androidx.compose.runtime.Stable',
'import androidx.compose.runtime.getValue',
'import androidx.compose.runtime.mutableStateOf',
'import androidx.compose.runtime.setValue',
'import androidx.compose.ui.graphics.Color',
'import androidx.compose.ui.unit.dp',
'',
'/**',
' * Auto-generated from design tokens. Do not edit manually.',
' */',
'internal class TangemShadowTokens {',
entries.join('\n\n'),
'}',
...classLines,
'',
].join('\n');
},
});
/**
* Kotlin format for TangemColors3 light/dark factory functions.
* Generates lightColors3() / darkColors3() functions referencing TangemColorPalette.
*/
StyleDictionary.registerFormat({
name: 'kotlin/compose-colors3-factory',
format: ({ dictionary, options }) => {
const themeName = options.themeName;
const funcName = `${themeName.toLowerCase()}Colors3`;
// Ensure tree is built (should already be set by class format in Light build)
if (!colors3StructureTree) {
const colorTokens = dictionary.allTokens.filter(
t => t.$type === 'color' && !isSourceOnlyToken(t),
);
colors3StructureTree = buildClassStructureTree(colorTokens);
resolveClassTreeConflicts(colors3StructureTree);
}
// Build value map: jsonPath → Kotlin palette reference or Color literal
const colorTokens = dictionary.allTokens.filter(
t => t.$type === 'color' && !isSourceOnlyToken(t),
);
const valueMap = new Map();
for (const token of colorTokens) {
const jsonPath = token.path.join('.');
valueMap.set(jsonPath, paletteRefOrColor(token));
}
const bodyLines = renderFactoryContent('TangemColors3', colors3StructureTree, valueMap, 2);
return [
FILE_SUPPRESS,
'',
`package ${PACKAGE}`,
'',
'import androidx.compose.ui.graphics.Color',
'',
'/**',
` * Auto-generated from design tokens. Do not edit manually.`,
` * Theme: ${themeName}`,
' */',
`internal fun ${funcName}() =`,
' TangemColors3(',
...bodyLines,
' )',
'',
].join('\n');
},
@ -425,6 +782,27 @@ const composePlatformTransforms = [
for (const [themeName, { sets }] of Object.entries(themeBuilds)) {
console.log(`\nBuilding ${themeName} color tokens...`);
const colorFilter = token => token.$type === 'color' && !isSourceOnlyToken(token);
const files = [];
// Only light build generates the class definition (canonical codeSyntax structure)
if (themeName === 'Light') {
files.push({
destination: 'TangemColors3.kt',
format: 'kotlin/compose-colors3-class',
filter: colorFilter,
});
}
// Both themes generate factory functions
files.push({
destination: `TangemColors3${themeName}.kt`,
format: 'kotlin/compose-colors3-factory',
options: { themeName },
filter: colorFilter,
});
const sd = new StyleDictionary({
source: sets.map(s => path.join(tokensDir, `${s}.json`)),
preprocessors: ['tokens-studio'],
@ -434,24 +812,44 @@ for (const [themeName, { sets }] of Object.entries(themeBuilds)) {
compose: {
transforms: composePlatformTransforms,
buildPath: outputDir + '/',
files: [
{
destination: `Tangem${themeName}ColorTokens.kt`,
format: 'kotlin/compose-colors',
options: { themeName },
filter: token => token.$type === 'color' && !isSourceOnlyToken(token),
},
],
files,
},
},
});
await sd.buildAllPlatforms();
console.log(` ✓ Tangem${themeName}ColorTokens.kt`);
if (themeName === 'Light') console.log(' ✓ TangemColors3.kt');
console.log(` ✓ TangemColors3${themeName}.kt`);
}
// Build theme-independent tokens (dimensions, opacity, typography, shadows)
console.log('\nBuilding dimension, opacity, typography, and shadow tokens...');
// Build palette tokens
console.log('\nBuilding palette tokens...');
const paletteSd = new StyleDictionary({
source: [...coreSets, 'semantic/size/opacity'].map(s => path.join(tokensDir, `${s}.json`)),
preprocessors: ['tokens-studio'],
usesDtcg: true,
log: { warnings: 'disabled', errors: { brokenReferences: 'console' } },
platforms: {
compose: {
transforms: composePlatformTransforms,
buildPath: outputDir + '/',
files: [
{
destination: 'TangemColorPalette.kt',
format: 'kotlin/compose-palette',
filter: token => token.$type === 'color' && token.path[0] === 'palette',
},
],
},
},
});
await paletteSd.buildAllPlatforms();
console.log(' ✓ TangemColorPalette.kt');
// Build theme-independent tokens (dimensions, typography)
console.log('\nBuilding dimension and typography tokens...');
const sd = new StyleDictionary({
source: sharedBuildSets.map(s => path.join(tokensDir, `${s}.json`)),
@ -464,42 +862,22 @@ const sd = new StyleDictionary({
buildPath: outputDir + '/',
files: [
{
destination: 'TangemDimensionTokens.kt',
format: 'kotlin/compose-dimensions',
filter: token => {
const t = token.$type;
return (
token.path[0] !== 'font' &&
(t === 'dimension' || t === 'borderRadius' || t === 'borderWidth') &&
!isSourceOnlyToken(token)
);
},
destination: 'TangemDimens3.kt',
format: 'kotlin/compose-dimens3',
},
{
destination: 'TangemOpacityTokens.kt',
format: 'kotlin/compose-opacity',
filter: token => token.$type === 'opacity' && !isSourceOnlyToken(token),
},
{
destination: 'TangemTypographyTokens.kt',
format: 'kotlin/compose-typography',
destination: 'TangemTypography3.kt',
format: 'kotlin/compose-typography3',
filter: token => token.$type === 'typography' && !isSourceOnlyToken(token),
},
{
destination: 'TangemShadowTokens.kt',
format: 'kotlin/compose-shadows',
filter: token => (token.$type === 'shadow' || token.$type === 'boxShadow') && !isSourceOnlyToken(token),
},
],
},
},
});
await sd.buildAllPlatforms();
console.log(' ✓ TangemDimensionTokens.kt');
console.log(' ✓ TangemOpacityTokens.kt');
console.log(' ✓ TangemTypographyTokens.kt');
console.log(' ✓ TangemShadowTokens.kt');
console.log(' ✓ TangemDimens3.kt');
console.log(' ✓ TangemTypography3.kt');
// ── Write source hash ─────────────────────────────────────────────────────────
// Hash all token JSON files so Gradle can verify generated code matches ds-tokens.
@ -513,7 +891,12 @@ function computeTokensHash() {
}
}
walk(tokensDir);
files.sort(); // deterministic order
// Sort by relative path with forward slashes to match Gradle's invariantSeparatorsPath sorting
files.sort((a, b) => {
const ra = path.relative(tokensDir, a).split(path.sep).join('/');
const rb = path.relative(tokensDir, b).split(path.sep).join('/');
return ra.localeCompare(rb);
});
const hash = crypto.createHash('sha256');
for (const file of files) {