Add Rarity to cosmetic items (#3587)

## Description:


https://github.com/user-attachments/assets/f2216dec-72aa-497a-89cc-169c2a40021e


* Fortnite-style rarity system for cosmetics: New CosmeticContainer
component applies tier-based visual styling (gradient backgrounds,
glowing borders, hover effects) to flag and pattern cards across 5
rarity tiers: Common, Uncommon, Rare, Epic, and Legendary
* Legendary hover effects: Scale-up animation, pulsing orange glow,
shimmer sweep, rotating border sweep, corner sparkles, and screen
dimming backdrop
* Epic hover effects: Purple shimmer sweep glint on hover
* Purchase button overhaul: Green ember particles on container hover
(non-common only), 40-particle burst stream on button hover, pulsating
green glow, shimmer streak animation, and loading spinner on click
* Clickable cosmetic cards: Clicking anywhere on a purchasable card (not
just the purchase button) triggers the purchase flow
* Refactored components: ArtistInfo renamed to CosmeticInfo (now shows
rarity and color palette in tooltip),
* Forward-compatible rarity schema: rarity field uses .or(z.string()) so
unknown backend values won't break the client


## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

## Please put your Discord username so you can be contacted if a bug or
regression is found:

evan
This commit is contained in:
Evan
2026-04-06 11:38:24 -07:00
committed by GitHub
parent 724d639011
commit 2d28bfcd01
12 changed files with 751 additions and 168 deletions
+11 -9
View File
@@ -51,8 +51,17 @@ export const ColorPaletteSchema = z.object({
secondaryColor: z.string(),
});
export const PatternSchema = z.object({
const CosmeticSchema = z.object({
name: CosmeticNameSchema,
affiliateCode: z.string().nullable(),
product: ProductSchema.nullable(),
artist: z.string().optional(),
rarity: z
.enum(["common", "uncommon", "rare", "epic", "legendary"])
.or(z.string()),
});
export const PatternSchema = CosmeticSchema.extend({
pattern: PatternDataSchema,
colorPalettes: z
.object({
@@ -61,17 +70,10 @@ export const PatternSchema = z.object({
})
.array()
.optional(),
affiliateCode: z.string().nullable(),
product: ProductSchema.nullable(),
artist: z.string().optional(),
});
export const FlagSchema = z.object({
name: CosmeticNameSchema,
export const FlagSchema = CosmeticSchema.extend({
url: z.string(),
affiliateCode: z.string().nullable(),
product: ProductSchema.nullable(),
artist: z.string().optional(),
});
// Schema for resources/cosmetics/cosmetics.json