feat: glow

This commit is contained in:
TrèsAbhi committed 2026-01-17 13:50:53 -06:00
1 parent 79b4f05809
commit dda63830ec
7 files changed
+110 -46

No files matched your search

+8
View File
@@ -51,5 +51,13 @@
"avatars": {
"search": "Search avatars...",
"no_description": "Avatar has no description."
},
"grades": {
"0": "Unspecified",
"1": "Common",
"2": "Rare",
"3": "Epic",
"4": "Legendary"
}
}
@@ -58,9 +58,31 @@
flex-direction: column;
align-items: center;
padding: var(--space-2);
&[data-grade="1"] .image {
outline: var(--space-2) solid var(--gray-9);
}
&[data-grade="2"] .image {
outline: var(--space-2) solid var(--blue-9);
box-shadow: 0 0 var(--space-3) var(--blue-9);
}
&[data-grade="3"] .image {
outline: var(--space-2) solid var(--purple-9);
box-shadow: 0 0 var(--space-4) var(--purple-9);
}
&[data-grade="4"] .image {
outline: var(--space-2) solid var(--amber-9);
box-shadow: 0 0 var(--space-5) var(--amber-9);
}
.image {
aspect-ratio: 7 / 8;
width: 8rem;
border-radius: var(--space-3);
background-size: cover;
background-position: center;
@@ -1,5 +1,7 @@
import { literals } from "@blitzkit/i18n";
import { Grade } from "@protos/blitz_static_standard_grades_enum";
import { Grade } from "@protos/auto_items";
import { StarFilledIcon } from "@radix-ui/react-icons";
import type { Color } from "../../core/ui/color";
import { useGameStrings } from "../../hooks/useGameStrings";
import { useStrings } from "../../hooks/useStrings";
import type { Avatar } from "../../protos/avatar";
@@ -20,9 +22,6 @@ const MAX_CARDS = 3;
const CARD_TRIM = "var(--space-3)";
export function AvatarCard(props: PropsWithSkeleton<Props>) {
const gameStrings = useGameStrings("ProfileAvatarEntity");
const strings = useStrings();
const card = (
<div className={styles.card}>
<Series skeleton={props.skeleton} {...props} />
@@ -43,47 +42,67 @@ export function AvatarCard(props: PropsWithSkeleton<Props>) {
{props.name}
</Text>
{[...props.avatars].reverse().map((avatar) => {
const description = gameStrings[avatar.stuff_ui!.description];
const obtaining = gameStrings[avatar.stuff_ui!.obtaining_methods];
const hasDescription = description || obtaining;
return (
<div className={styles.details}>
<div
className={styles.preview}
data-grade={avatar.stuff_ui!.grade}
>
<div
className={styles.image}
style={{
backgroundImage: `url(/api/avatars/${avatar.name}.webp)`,
}}
/>
<Badge className={styles.grade}>
{Grade[avatar.stuff_ui!.grade]}
</Badge>
</div>
<div className={styles.description}>
<Text>
{hasDescription ? (
<>
{description} {obtaining}
</>
) : (
strings.avatars.no_description
)}
</Text>
</div>
</div>
);
})}
<Details avatars={props.avatars} />
</Dialog.Content>
</Dialog.Root>
);
}
interface DetailsProps {
avatars: Avatar[];
}
const gradeColors: Record<Grade, Color> = {
[Grade.GRADE_GRADE_UNSPECIFIED]: "gray",
[Grade.GRADE_GRADE_COMMON]: "gray",
[Grade.GRADE_GRADE_RARE]: "blue",
[Grade.GRADE_GRADE_EPIC]: "purple",
[Grade.GRADE_GRADE_LEGENDARY]: "amber",
};
function Details({ avatars }: DetailsProps) {
const gameStrings = useGameStrings("ProfileAvatarEntity");
const strings = useStrings();
return [...avatars].reverse().map((avatar) => {
const description = gameStrings[avatar.stuff_ui!.description];
const obtaining = gameStrings[avatar.stuff_ui!.obtaining_methods];
const hasDescription = description || obtaining;
return (
<div className={styles.details}>
<div className={styles.preview} data-grade={avatar.stuff_ui!.grade}>
<div
className={styles.image}
style={{
backgroundImage: `url(/api/avatars/${avatar.name}.webp)`,
}}
/>
<Badge
color={gradeColors[avatar.stuff_ui!.grade]}
className={styles.grade}
>
<StarFilledIcon />
{strings.grades[avatar.stuff_ui!.grade]}
</Badge>
</div>
<div className={styles.description}>
<Text>
{hasDescription ? (
<>
{description} {obtaining}
</>
) : (
strings.avatars.no_description
)}
</Text>
</div>
</div>
);
});
}
interface SeriesProps {
avatars: Avatar[];
}
@@ -5,4 +5,13 @@
font-size: var(--font-size-minor);
font-weight: var(--weight-medium);
display: flex;
align-items: center;
gap: var(--space-2);
.svg {
width: 1em;
height: 1em;
}
}
@@ -1,7 +1,8 @@
import type { ComponentProps } from "react";
import { classNames } from "../../core/ui/classNames";
import type { Color } from "../../core/ui/color";
import "./index.css";
import { LIGHT_TEXT_COLORS } from "../Button";
import styles from "./index.module.css";
export interface BadgeProps extends ComponentProps<"div"> {
color?: Color | "accent";
@@ -15,13 +16,14 @@ export function Badge({
highContrast,
...props
}: BadgeProps) {
const lightText = LIGHT_TEXT_COLORS.has(color as Color);
return (
<div
className={classNames("badge", className)}
className={classNames(styles.badge, className)}
style={{
backgroundColor: `var(--${color}-${highContrast ? 12 : 9})`,
color: `var(--gray-${highContrast ? 1 : 12})`,
color: `var(--gray-${highContrast || lightText ? 1 : 12})`,
...style,
}}
{...props}
@@ -8,12 +8,12 @@ export interface ButtonProps extends ComponentProps<"button"> {
variant?: "solid";
}
const LIGHT_TEXT_COLORS = new Set<Color>([
"purple",
export const LIGHT_TEXT_COLORS = new Set<Color>([
"gold",
"bronze",
"teal",
"grass",
"amber",
]);
export function Button({
@@ -1,4 +1,5 @@
---
import ColorScale from "../../../components/ColorScale.astro";
import ToolBanner from "../../../components/ToolBanner.astro";
import { tools } from "../../../core/tools/tools";
import BaseLayout from "../../../layouts/BaseLayout.astro";
@@ -10,6 +11,9 @@ const { locale } = Astro.props;
---
<BaseLayout {locale} accent="gold">
<ColorScale color="purple" />
<ColorScale color="blue" />
<ToolBanner {locale} tool={tools.avatars} slot="pre-app" />
<Page {locale} client:load />
</BaseLayout>