refactor: remove vanilla-extract

This commit is contained in:
Hachi-R 2025-02-01 03:00:14 -03:00
parent e4631bba7b
commit f6e4852f4a
16 changed files with 373 additions and 547 deletions

View File

@ -42,9 +42,6 @@
"@reduxjs/toolkit": "^2.2.3", "@reduxjs/toolkit": "^2.2.3",
"@sentry/react": "^8.47.0", "@sentry/react": "^8.47.0",
"@sentry/vite-plugin": "^2.22.7", "@sentry/vite-plugin": "^2.22.7",
"@vanilla-extract/css": "^1.14.2",
"@vanilla-extract/dynamic": "^2.1.2",
"@vanilla-extract/recipes": "^0.5.2",
"auto-launch": "^5.0.6", "auto-launch": "^5.0.6",
"axios": "^1.7.9", "axios": "^1.7.9",
"better-sqlite3": "^11.7.0", "better-sqlite3": "^11.7.0",
@ -101,7 +98,6 @@
"@types/react-dom": "^18.2.18", "@types/react-dom": "^18.2.18",
"@types/sound-play": "^1.1.3", "@types/sound-play": "^1.1.3",
"@types/user-agents": "^1.0.4", "@types/user-agents": "^1.0.4",
"@vanilla-extract/vite-plugin": "^4.0.7",
"@vitejs/plugin-react": "^4.2.1", "@vitejs/plugin-react": "^4.2.1",
"electron": "^31.7.6", "electron": "^31.7.6",
"electron-builder": "^25.1.8", "electron-builder": "^25.1.8",

View File

@ -1,134 +0,0 @@
import {
ComplexStyleRule,
createContainer,
globalStyle,
style,
} from "@vanilla-extract/css";
import { SPACING_UNIT, vars } from "./theme.css";
export const appContainer = createContainer();
globalStyle("*", {
boxSizing: "border-box",
});
globalStyle("::-webkit-scrollbar", {
width: "9px",
backgroundColor: vars.color.darkBackground,
});
globalStyle("::-webkit-scrollbar-track", {
backgroundColor: "rgba(255, 255, 255, 0.03)",
});
globalStyle("::-webkit-scrollbar-thumb", {
backgroundColor: "rgba(255, 255, 255, 0.08)",
borderRadius: "24px",
});
globalStyle("::-webkit-scrollbar-thumb:hover", {
backgroundColor: "rgba(255, 255, 255, 0.16)",
});
globalStyle("html, body, #root, main", {
height: "100%",
});
globalStyle("body", {
overflow: "hidden",
userSelect: "none",
fontFamily: "Noto Sans, sans-serif",
fontSize: vars.size.body,
color: vars.color.body,
margin: "0",
});
globalStyle("button", {
padding: "0",
backgroundColor: "transparent",
border: "none",
fontFamily: "inherit",
});
globalStyle("h1, h2, h3, h4, h5, h6, p", {
margin: 0,
});
globalStyle("p", {
lineHeight: "20px",
});
globalStyle("#root, main", {
display: "flex",
});
globalStyle("#root", {
flexDirection: "column",
});
globalStyle("main", {
overflow: "hidden",
});
globalStyle(
"input::-webkit-outer-spin-button, input::-webkit-inner-spin-button",
{
WebkitAppearance: "none",
margin: "0",
}
);
globalStyle("label", {
fontSize: vars.size.body,
});
globalStyle("input[type=number]", {
MozAppearance: "textfield",
});
globalStyle("img", {
WebkitUserDrag: "none",
} as Record<string, string>);
globalStyle("progress[value]", {
WebkitAppearance: "none",
});
export const container = style({
width: "100%",
height: "100%",
overflow: "hidden",
display: "flex",
flexDirection: "column",
containerName: appContainer,
containerType: "inline-size",
});
export const content = style({
overflowY: "auto",
alignItems: "center",
display: "flex",
flexDirection: "column",
position: "relative",
height: "100%",
background: `linear-gradient(0deg, ${vars.color.darkBackground} 50%, ${vars.color.background} 100%)`,
});
export const titleBar = style({
display: "flex",
width: "100%",
height: "35px",
minHeight: "35px",
backgroundColor: vars.color.darkBackground,
alignItems: "center",
padding: `0 ${SPACING_UNIT * 2}px`,
WebkitAppRegion: "drag",
zIndex: "4",
borderBottom: `1px solid ${vars.color.border}`,
} as ComplexStyleRule);
export const cloudText = style({
background: "linear-gradient(270deg, #16B195 50%, #3E62C0 100%)",
backgroundClip: "text",
color: "transparent",
});

View File

@ -35,12 +35,23 @@ $toast-height: 80px;
align-items: center; align-items: center;
} }
&__message-container {
display: flex;
gap: globals.$spacing-unit;
}
&__message {
font-weight: bold;
}
&__progress { &__progress {
width: 100%; width: 100%;
height: 5px; height: 5px;
&::-webkit-progress-bar { &::-webkit-progress-bar {
background-color: globals.$dark-background-color; background-color: globals.$dark-background-color;
} }
&::-webkit-progress-value { &::-webkit-progress-value {
background-color: globals.$muted-color; background-color: globals.$muted-color;
} }

View File

@ -7,7 +7,6 @@ import {
} from "@primer/octicons-react"; } from "@primer/octicons-react";
import "./toast.scss"; import "./toast.scss";
import { SPACING_UNIT } from "@renderer/theme.css";
import cn from "classnames"; import cn from "classnames";
export interface ToastProps { export interface ToastProps {
@ -84,7 +83,7 @@ export function Toast({ visible, message, type, onClose }: ToastProps) {
})} })}
> >
<div className="toast__content"> <div className="toast__content">
<div style={{ display: "flex", gap: `${SPACING_UNIT}px` }}> <div className="toast__message-container">
{type === "success" && ( {type === "success" && (
<CheckCircleFillIcon className="toast__success-icon" /> <CheckCircleFillIcon className="toast__success-icon" />
)} )}
@ -94,7 +93,7 @@ export function Toast({ visible, message, type, onClose }: ToastProps) {
)} )}
{type === "warning" && <AlertIcon className="toast__warning-icon" />} {type === "warning" && <AlertIcon className="toast__warning-icon" />}
<span style={{ fontWeight: "bold" }}>{message}</span> <span className="toast__message">{message}</span>
</div> </div>
<button <button

View File

@ -0,0 +1,248 @@
@use "../../scss/globals.scss";
$hero-height: 150px;
$logo-height: 100px;
$logo-max-width: 200px;
.achievements-content {
&__comparison {
display: flex;
gap: globals.$spacing-unit * 2;
align-items: center;
position: relative;
padding: globals.$spacing-unit;
&__container {
position: absolute;
z-index: 2;
inset: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
flex-direction: row;
gap: globals.$spacing-unit;
border-radius: 4px;
justify-content: center;
&__subscription-required-button {
text-decoration: none;
display: flex;
justify-content: center;
width: 100%;
gap: globals.$spacing-unit / 2;
color: globals.$body-color;
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
}
&__blured-avatar {
display: flex;
gap: globals.$spacing-unit * 2;
align-items: center;
height: 62px;
position: relative;
filter: blur(4px);
h1 {
margin-bottom: 8px;
}
}
&__small-avatar {
height: 32px;
width: 32px;
border-radius: 4px;
display: flex;
justify-content: center;
align-items: center;
position: relative;
object-fit: cover;
}
}
&__subscription-required-button {
text-decoration: none;
display: flex;
justify-content: center;
width: 100%;
gap: 8px;
color: globals.$body-color;
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
&__user-summary {
display: flex;
gap: globals.$spacing-unit * 2;
align-items: center;
padding: globals.$spacing-unit globals.$spacing-unit * 2;
&__container {
display: flex;
flex-direction: column;
width: 100%;
h1 {
margin-bottom: 8px;
}
&__stats {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
color: globals.$muted-color;
&__trophy-count {
display: flex;
align-items: center;
gap: 8px;
}
&__progress-bar {
width: 100%;
height: 8px;
transition: all ease 0.2s;
&::-webkit-progress-bar {
background-color: rgba(255, 255, 255, 0.15);
border-radius: 4px;
}
&::-webkit-progress-value {
background-color: globals.$muted-color;
border-radius: 4px;
}
}
}
}
}
&__achievements-list {
display: flex;
flex-direction: column;
overflow: hidden;
width: 100%;
height: 100%;
transition: all ease 0.3s;
&__image {
display: none;
}
&__section {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
overflow: auto;
z-index: 1;
&__container {
display: flex;
flex-direction: column;
background: linear-gradient(
0deg,
globals.$background-color 0%,
globals.$background-color 100%
);
&__hero {
width: 100%;
height: $hero-height;
min-height: $hero-height;
display: flex;
flex-direction: column;
position: relative;
transition: all ease 0.2s;
&__content {
padding: globals.$spacing-unit * 2;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
&__game-logo {
width: $logo-max-width;
height: $logo-height;
object-fit: contain;
transition: all ease 0.2s;
&:hover {
transform: scale(1.05);
}
}
}
}
&__achievements-summary-wrapper {
display: flex;
flex-direction: column;
width: 100%;
gap: globals.$spacing-unit;
padding: globals.$spacing-unit;
}
}
&__table-header {
width: 100%;
background-color: globals.$background-color;
transition: all ease 0.2s;
border-bottom: 1px solid globals.$border-color;
position: sticky;
top: 0;
z-index: 1;
&--stuck {
box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, 0.8);
}
&__container {
display: grid;
gap: globals.$spacing-unit * 2;
padding: globals.$spacing-unit * 3;
&--has-no-active-subscription {
grid-template-columns: 3fr 2fr;
}
&--has-active-subscription {
grid-template-columns: 3fr 1fr 1fr;
}
&__user-avatar {
display: flex;
justify-content: center;
}
&__other-user-avatar {
display: flex;
justify-content: center;
}
}
}
}
}
&__profile-avatar {
height: 54px;
width: 54px;
border-radius: 4px;
display: flex;
justify-content: center;
align-items: center;
background-color: globals.$background-color;
position: relative;
object-fit: cover;
}
}

View File

@ -8,18 +8,17 @@ import {
formatDownloadProgress, formatDownloadProgress,
} from "@renderer/helpers"; } from "@renderer/helpers";
import { LockIcon, PersonIcon, TrophyIcon } from "@primer/octicons-react"; import { LockIcon, PersonIcon, TrophyIcon } from "@primer/octicons-react";
import { SPACING_UNIT, vars } from "@renderer/theme.css";
import { gameDetailsContext } from "@renderer/context"; import { gameDetailsContext } from "@renderer/context";
import type { ComparedAchievements } from "@types"; import type { ComparedAchievements } from "@types";
import { average } from "color.js"; import { average } from "color.js";
import Color from "color"; import Color from "color";
import { Link } from "@renderer/components"; import { Link } from "@renderer/components";
import { ComparedAchievementList } from "./compared-achievement-list"; import { ComparedAchievementList } from "./compared-achievement-list";
import * as styles from "./achievements.css";
import { AchievementList } from "./achievement-list"; import { AchievementList } from "./achievement-list";
import { AchievementPanel } from "./achievement-panel"; import { AchievementPanel } from "./achievement-panel";
import { ComparedAchievementPanel } from "./compared-achievement-panel"; import { ComparedAchievementPanel } from "./compared-achievement-panel";
import { useSubscription } from "@renderer/hooks/use-subscription"; import { useSubscription } from "@renderer/hooks/use-subscription";
import "./achievements-content.scss";
interface UserInfo { interface UserInfo {
id: string; id: string;
@ -48,10 +47,10 @@ function AchievementSummary({ user, isComparison }: AchievementSummaryProps) {
user: Pick<UserInfo, "profileImageUrl" | "displayName"> user: Pick<UserInfo, "profileImageUrl" | "displayName">
) => { ) => {
return ( return (
<div className={styles.profileAvatar}> <div className="achievements-content__profile-avatar">
{user.profileImageUrl ? ( {user.profileImageUrl ? (
<img <img
className={styles.profileAvatar} className="achievements-content__profile-avatar"
src={user.profileImageUrl} src={user.profileImageUrl}
alt={user.displayName} alt={user.displayName}
/> />
@ -64,91 +63,33 @@ function AchievementSummary({ user, isComparison }: AchievementSummaryProps) {
if (isComparison && userDetails?.id == user.id && !hasActiveSubscription) { if (isComparison && userDetails?.id == user.id && !hasActiveSubscription) {
return ( return (
<div <div className="achievements-content__comparison">
style={{ <div className="achievements-content__comparison__container">
display: "flex",
gap: `${SPACING_UNIT * 2}px`,
alignItems: "center",
position: "relative",
padding: `${SPACING_UNIT}px`,
}}
>
<div
style={{
position: "absolute",
zIndex: 2,
inset: 0,
width: "100%",
height: "100%",
background: "rgba(0, 0, 0, 0.7)",
display: "flex",
alignItems: "center",
flexDirection: "row",
gap: `${SPACING_UNIT}px`,
borderRadius: "4px",
justifyContent: "center",
}}
>
<LockIcon size={24} /> <LockIcon size={24} />
<h3> <h3>
<button <button
className={styles.subscriptionRequiredButton} className="achievements-content__comparison__container__subscription-required-button"
onClick={() => showHydraCloudModal("achievements")} onClick={() => showHydraCloudModal("achievements")}
> >
{t("subscription_needed")} {t("subscription_needed")}
</button> </button>
</h3> </h3>
</div> </div>
<div <div className="achievements-content__comparison__blured-avatar">
style={{
display: "flex",
gap: `${SPACING_UNIT * 2}px`,
alignItems: "center",
height: "62px",
position: "relative",
filter: "blur(4px)",
}}
>
{getProfileImage(user)} {getProfileImage(user)}
<h1 style={{ marginBottom: "8px" }}>{user.displayName}</h1> <h1>{user.displayName}</h1>
</div> </div>
</div> </div>
); );
} }
return ( return (
<div <div className="achievements-content__user-summary">
style={{
display: "flex",
gap: `${SPACING_UNIT * 2}px`,
alignItems: "center",
padding: `${SPACING_UNIT}px`,
}}
>
{getProfileImage(user)} {getProfileImage(user)}
<div <div className="achievements-content__user-summary__container">
style={{ <h1>{user.displayName}</h1>
display: "flex", <div className="achievements-content__user-summary__container__stats">
flexDirection: "column", <div className="achievements-content__user-summary__container__stats__trophy-count">
width: "100%",
}}
>
<h1 style={{ marginBottom: "8px" }}>{user.displayName}</h1>
<div
style={{
display: "flex",
justifyContent: "space-between",
marginBottom: 8,
color: vars.color.muted,
}}
>
<div
style={{
display: "flex",
alignItems: "center",
gap: 8,
}}
>
<TrophyIcon size={13} /> <TrophyIcon size={13} />
<span> <span>
{user.unlockedAchievementCount} / {user.totalAchievementCount} {user.unlockedAchievementCount} / {user.totalAchievementCount}
@ -164,7 +105,7 @@ function AchievementSummary({ user, isComparison }: AchievementSummaryProps) {
<progress <progress
max={1} max={1}
value={user.unlockedAchievementCount / user.totalAchievementCount} value={user.unlockedAchievementCount / user.totalAchievementCount}
className={styles.achievementsProgressBar} className="achievements-content__user-summary__container__stats__progress-bar"
/> />
</div> </div>
</div> </div>
@ -203,7 +144,7 @@ export function AchievementsContent({
}; };
const onScroll: React.UIEventHandler<HTMLElement> = (event) => { const onScroll: React.UIEventHandler<HTMLElement> = (event) => {
const heroHeight = heroRef.current?.clientHeight ?? styles.HERO_HEIGHT; const heroHeight = heroRef.current?.clientHeight ?? 150;
const scrollY = (event.target as HTMLDivElement).scrollTop; const scrollY = (event.target as HTMLDivElement).scrollTop;
if (scrollY >= heroHeight && !isHeaderStuck) { if (scrollY >= heroHeight && !isHeaderStuck) {
@ -219,10 +160,10 @@ export function AchievementsContent({
user: Pick<UserInfo, "profileImageUrl" | "displayName"> user: Pick<UserInfo, "profileImageUrl" | "displayName">
) => { ) => {
return ( return (
<div className={styles.profileAvatarSmall}> <div className="achievements-content__comparison__small-avatar">
{user.profileImageUrl ? ( {user.profileImageUrl ? (
<img <img
className={styles.profileAvatarSmall} className="achievements-content__comparison__small-avatar"
src={user.profileImageUrl} src={user.profileImageUrl}
alt={user.displayName} alt={user.displayName}
/> />
@ -236,10 +177,10 @@ export function AchievementsContent({
if (!objectId || !shop || !gameTitle || !userDetails) return null; if (!objectId || !shop || !gameTitle || !userDetails) return null;
return ( return (
<div className={styles.wrapper}> <div className="achievements-content__achievements-list">
<img <img
src={steamUrlBuilder.libraryHero(objectId)} src={steamUrlBuilder.libraryHero(objectId)}
style={{ display: "none" }} className="achievements-content__achievements-list__image"
alt={gameTitle} alt={gameTitle}
onLoad={handleHeroLoad} onLoad={handleHeroLoad}
/> />
@ -247,38 +188,32 @@ export function AchievementsContent({
<section <section
ref={containerRef} ref={containerRef}
onScroll={onScroll} onScroll={onScroll}
className={styles.container} className="achievements-content__achievements-list__section"
> >
<div <div
className="achievements-content__achievements-list__section__container"
style={{ style={{
display: "flex", background: `linear-gradient(0deg, #151515 0%, ${gameColor} 100%)`,
flexDirection: "column",
background: `linear-gradient(0deg, ${vars.color.darkBackground} 0%, ${gameColor} 100%)`,
}} }}
> >
<div ref={heroRef} className={styles.hero}> <div
<div className={styles.heroContent}> ref={heroRef}
className="achievements-content__achievements-list__section__container__hero"
>
<div className="achievements-content__achievements-list__section__container__hero__content">
<Link <Link
to={buildGameDetailsPath({ shop, objectId, title: gameTitle })} to={buildGameDetailsPath({ shop, objectId, title: gameTitle })}
> >
<img <img
src={steamUrlBuilder.logo(objectId)} src={steamUrlBuilder.logo(objectId)}
className={styles.gameLogo} className="achievements-content__achievements-list__section__container__hero__content__game-logo"
alt={gameTitle} alt={gameTitle}
/> />
</Link> </Link>
</div> </div>
</div> </div>
<div <div className="achievements-content__achievements-list__section__container__achievements-summary-wrapper">
style={{
display: "flex",
flexDirection: "column",
width: "100%",
gap: `${SPACING_UNIT}px`,
padding: `${SPACING_UNIT}px`,
}}
>
<AchievementSummary <AchievementSummary
user={{ user={{
...userDetails, ...userDetails,
@ -298,24 +233,19 @@ export function AchievementsContent({
</div> </div>
{otherUser && ( {otherUser && (
<div className={styles.tableHeader({ stuck: isHeaderStuck })}> <div
className={`achievements-content__achievements-list__section__table-header ${isHeaderStuck ? "achievements-content__achievements-list__section__table-header--stuck" : ""}`}
>
<div <div
style={{ className={`achievements-content__achievements-list__section__table-header__container ${hasActiveSubscription ? "achievements-content__achievements-list__section__table-header__container--has-active-subscription" : "achievements-content__achievements-list__section__table-header__container--has-no-active-subscription"}`}
display: "grid",
gridTemplateColumns: hasActiveSubscription
? "3fr 1fr 1fr"
: "3fr 2fr",
gap: `${SPACING_UNIT * 2}px`,
padding: `${SPACING_UNIT}px ${SPACING_UNIT * 3}px`,
}}
> >
<div></div> <div></div>
{hasActiveSubscription && ( {hasActiveSubscription && (
<div style={{ display: "flex", justifyContent: "center" }}> <div className="achievements-content__achievements-list__section__table-header__container__user-avatar">
{getProfileImage({ ...userDetails })} {getProfileImage({ ...userDetails })}
</div> </div>
)} )}
<div style={{ display: "flex", justifyContent: "center" }}> <div className="achievements-content__achievements-list__section__table-header__container__other-user-avatar">
{getProfileImage(otherUser)} {getProfileImage(otherUser)}
</div> </div>
</div> </div>

View File

@ -1,197 +0,0 @@
import { SPACING_UNIT, vars } from "../../theme.css";
import { style } from "@vanilla-extract/css";
import { recipe } from "@vanilla-extract/recipes";
export const HERO_HEIGHT = 150;
const LOGO_HEIGHT = 100;
const LOGO_MAX_WIDTH = 200;
export const wrapper = style({
display: "flex",
flexDirection: "column",
overflow: "hidden",
width: "100%",
height: "100%",
transition: "all ease 0.3s",
});
export const hero = style({
width: "100%",
height: `${HERO_HEIGHT}px`,
minHeight: `${HERO_HEIGHT}px`,
display: "flex",
flexDirection: "column",
position: "relative",
transition: "all ease 0.2s",
});
export const heroContent = style({
padding: `${SPACING_UNIT * 2}px`,
width: "100%",
display: "flex",
justifyContent: "space-between",
alignItems: "center",
});
export const gameLogo = style({
width: LOGO_MAX_WIDTH,
height: LOGO_HEIGHT,
objectFit: "contain",
transition: "all ease 0.2s",
":hover": {
transform: "scale(1.05)",
},
});
export const container = style({
width: "100%",
height: "100%",
display: "flex",
flexDirection: "column",
overflow: "auto",
zIndex: "1",
});
export const tableHeader = recipe({
base: {
width: "100%",
backgroundColor: vars.color.darkBackground,
transition: "all ease 0.2s",
borderBottom: `solid 1px ${vars.color.border}`,
position: "sticky",
top: "0",
zIndex: "1",
},
variants: {
stuck: {
true: {
boxShadow: "0px 0px 15px 0px rgba(0, 0, 0, 0.8)",
},
},
},
});
export const list = style({
listStyle: "none",
margin: "0",
display: "flex",
flexDirection: "column",
gap: `${SPACING_UNIT * 2}px`,
padding: `${SPACING_UNIT * 2}px`,
width: "100%",
backgroundColor: vars.color.background,
});
export const listItem = style({
transition: "all ease 0.1s",
color: vars.color.muted,
width: "100%",
overflow: "hidden",
borderRadius: "4px",
padding: `${SPACING_UNIT}px ${SPACING_UNIT}px`,
gap: `${SPACING_UNIT * 2}px`,
alignItems: "center",
textAlign: "left",
":hover": {
backgroundColor: "rgba(255, 255, 255, 0.15)",
textDecoration: "none",
},
});
export const listItemImage = recipe({
base: {
width: "54px",
height: "54px",
borderRadius: "4px",
objectFit: "cover",
},
variants: {
unlocked: {
false: {
filter: "grayscale(100%)",
},
},
},
});
export const achievementsProgressBar = style({
width: "100%",
height: "8px",
transition: "all ease 0.2s",
"::-webkit-progress-bar": {
backgroundColor: "rgba(255, 255, 255, 0.15)",
borderRadius: "4px",
},
"::-webkit-progress-value": {
backgroundColor: vars.color.muted,
borderRadius: "4px",
},
});
export const heroLogoBackdrop = style({
width: "100%",
height: "100%",
position: "absolute",
display: "flex",
flexDirection: "column",
justifyContent: "flex-end",
});
export const heroImageSkeleton = style({
height: "150px",
});
export const heroPanelSkeleton = style({
width: "100%",
padding: `${SPACING_UNIT * 2}px ${SPACING_UNIT * 2}px`,
display: "flex",
alignItems: "center",
backgroundColor: vars.color.background,
height: "72px",
borderBottom: `solid 1px ${vars.color.border}`,
});
export const listItemSkeleton = style({
width: "100%",
overflow: "hidden",
borderRadius: "4px",
padding: `${SPACING_UNIT}px ${SPACING_UNIT}px`,
gap: `${SPACING_UNIT * 2}px`,
});
export const profileAvatar = style({
height: "54px",
width: "54px",
borderRadius: "4px",
display: "flex",
justifyContent: "center",
alignItems: "center",
backgroundColor: vars.color.background,
position: "relative",
objectFit: "cover",
});
export const profileAvatarSmall = style({
height: "32px",
width: "32px",
borderRadius: "4px",
display: "flex",
justifyContent: "center",
alignItems: "center",
backgroundColor: vars.color.background,
position: "relative",
objectFit: "cover",
});
export const subscriptionRequiredButton = style({
textDecoration: "none",
display: "flex",
justifyContent: "center",
width: "100%",
gap: `${SPACING_UNIT / 2}px`,
color: vars.color.body,
cursor: "pointer",
":hover": {
textDecoration: "underline",
},
});

View File

@ -3,7 +3,6 @@ import { useAppDispatch, useUserDetails } from "@renderer/hooks";
import type { ComparedAchievements, GameShop } from "@types"; import type { ComparedAchievements, GameShop } from "@types";
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { useSearchParams } from "react-router-dom"; import { useSearchParams } from "react-router-dom";
import { vars } from "@renderer/theme.css";
import { import {
GameDetailsContextConsumer, GameDetailsContextConsumer,
GameDetailsContextProvider, GameDetailsContextProvider,
@ -75,10 +74,7 @@ export default function Achievements() {
(otherUserId && comparedAchievements === null); (otherUserId && comparedAchievements === null);
return ( return (
<SkeletonTheme <SkeletonTheme baseColor="#1c1c1c" highlightColor="#444">
baseColor={vars.color.background}
highlightColor="#444"
>
{showSkeleton ? ( {showSkeleton ? (
<AchievementsSkeleton /> <AchievementsSkeleton />
) : ( ) : (

View File

@ -19,4 +19,62 @@
border: 1px solid globals.$border-color; border: 1px solid globals.$border-color;
align-self: flex-start; align-self: flex-start;
} }
&__header {
display: flex;
gap: 8px;
align-items: center;
justify-content: space-between;
}
&__filters-wrapper {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
&__filters-list {
display: flex;
gap: 8px;
flex-wrap: wrap;
list-style: none;
margin: 0;
padding: 0;
}
&__content {
display: flex;
gap: calc(globals.$spacing-unit * 2);
justify-content: space-between;
}
&__games-container {
display: flex;
flex-direction: column;
width: 100%;
gap: 8px;
}
&__pagination-container {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 16px;
}
&__result-count {
font-size: 12px;
}
&__filters-sections {
display: flex;
flex-direction: column;
gap: 16px;
}
&__skeleton {
height: 105px;
border-radius: 4px;
border: solid 1px rgba(255, 255, 255, 0.15);
}
} }

View File

@ -10,7 +10,6 @@ import { useEffect, useMemo, useRef, useState } from "react";
import "./catalogue.scss"; import "./catalogue.scss";
import { SPACING_UNIT, vars } from "@renderer/theme.css";
import { downloadSourcesTable } from "@renderer/dexie"; import { downloadSourcesTable } from "@renderer/dexie";
import { FilterSection } from "./filter-section"; import { FilterSection } from "./filter-section";
import { setFilters, setPage } from "@renderer/features"; import { setFilters, setPage } from "@renderer/features";
@ -230,25 +229,9 @@ export default function Catalogue() {
return ( return (
<div className="catalogue" ref={cataloguePageRef}> <div className="catalogue" ref={cataloguePageRef}>
<div <div className="catalogue__header">
style={{ <div className="catalogue__filters-wrapper">
display: "flex", <ul className="catalogue__filters-list">
gap: 8,
alignItems: "center",
justifyContent: "space-between",
}}
>
<div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
<ul
style={{
display: "flex",
gap: 8,
flexWrap: "wrap",
listStyle: "none",
margin: 0,
padding: 0,
}}
>
{groupedFilters.map((filter) => ( {groupedFilters.map((filter) => (
<li key={`${filter.key}-${filter.value}`}> <li key={`${filter.key}-${filter.value}`}>
<FilterItem <FilterItem
@ -270,50 +253,20 @@ export default function Catalogue() {
</div> </div>
</div> </div>
<div <div className="catalogue__content">
style={{ <div className="catalogue__games-container">
display: "flex",
gap: SPACING_UNIT * 2,
justifyContent: "space-between",
}}
>
<div
style={{
display: "flex",
flexDirection: "column",
width: "100%",
gap: 8,
}}
>
{isLoading ? ( {isLoading ? (
<SkeletonTheme <SkeletonTheme baseColor="#1c1c1c" highlightColor="#444">
baseColor={vars.color.darkBackground}
highlightColor={vars.color.background}
>
{Array.from({ length: PAGE_SIZE }).map((_, i) => ( {Array.from({ length: PAGE_SIZE }).map((_, i) => (
<Skeleton <Skeleton key={i} className="catalogue__skeleton" />
key={i}
style={{
height: 105,
borderRadius: 4,
border: `solid 1px ${vars.color.border}`,
}}
/>
))} ))}
</SkeletonTheme> </SkeletonTheme>
) : ( ) : (
results.map((game) => <GameItem key={game.id} game={game} />) results.map((game) => <GameItem key={game.id} game={game} />)
)} )}
<div <div className="catalogue__pagination-container">
style={{ <span className="catalogue__result-count">
display: "flex",
alignItems: "center",
justifyContent: "space-between",
marginTop: 16,
}}
>
<span style={{ fontSize: 12 }}>
{t("result_count", { {t("result_count", {
resultCount: formatNumber(itemsCount), resultCount: formatNumber(itemsCount),
})} })}
@ -333,7 +286,7 @@ export default function Catalogue() {
</div> </div>
<div className="catalogue__filters-container"> <div className="catalogue__filters-container">
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}> <div className="catalogue__filters-sections">
{filterSections.map((section) => ( {filterSections.map((section) => (
<FilterSection <FilterSection
key={section.key} key={section.key}

View File

@ -12,8 +12,6 @@ import { useTranslation } from "react-i18next";
import { SkeletonTheme } from "react-loading-skeleton"; import { SkeletonTheme } from "react-loading-skeleton";
import { GameDetailsSkeleton } from "./game-details-skeleton"; import { GameDetailsSkeleton } from "./game-details-skeleton";
import { vars } from "@renderer/theme.css";
import { GameDetailsContent } from "./game-details-content"; import { GameDetailsContent } from "./game-details-content";
import { import {
CloudSyncContextConsumer, CloudSyncContextConsumer,
@ -148,10 +146,7 @@ export default function GameDetails() {
)} )}
</CloudSyncContextConsumer> </CloudSyncContextConsumer>
<SkeletonTheme <SkeletonTheme baseColor="#1c1c1c" highlightColor="#444">
baseColor={vars.color.background}
highlightColor="#444"
>
{isLoading ? <GameDetailsSkeleton /> : <GameDetailsContent />} {isLoading ? <GameDetailsSkeleton /> : <GameDetailsContent />}
<RepacksModal <RepacksModal

View File

@ -1,6 +1,10 @@
@use "../../../scss/globals.scss"; @use "../../../scss/globals.scss";
.repacks-modal { .repacks-modal {
&__filter-container {
margin-bottom: calc(globals.$spacing-unit * 2);
}
&__repacks { &__repacks {
display: flex; display: flex;
gap: globals.$spacing-unit; gap: globals.$spacing-unit;

View File

@ -4,7 +4,6 @@ import { useTranslation } from "react-i18next";
import { Badge, Button, Modal, TextField } from "@renderer/components"; import { Badge, Button, Modal, TextField } from "@renderer/components";
import type { GameRepack } from "@types"; import type { GameRepack } from "@types";
import { SPACING_UNIT } from "@renderer/theme.css";
import { DownloadSettingsModal } from "./download-settings-modal"; import { DownloadSettingsModal } from "./download-settings-modal";
import { gameDetailsContext } from "@renderer/context"; import { gameDetailsContext } from "@renderer/context";
import { Downloader } from "@shared"; import { Downloader } from "@shared";
@ -85,7 +84,7 @@ export function RepacksModal({
description={t("repacks_modal_description")} description={t("repacks_modal_description")}
onClose={onClose} onClose={onClose}
> >
<div style={{ marginBottom: `${SPACING_UNIT * 2}px` }}> <div className="repacks-modal__filter-container">
<TextField placeholder={t("filter")} onChange={handleFilter} /> <TextField placeholder={t("filter")} onChange={handleFilter} />
</div> </div>

View File

@ -1,7 +1,6 @@
import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; import Skeleton, { SkeletonTheme } from "react-loading-skeleton";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import type { HowLongToBeatCategory } from "@types"; import type { HowLongToBeatCategory } from "@types";
import { vars } from "@renderer/theme.css";
import { SidebarSection } from "../sidebar-section/sidebar-section"; import { SidebarSection } from "../sidebar-section/sidebar-section";
import "./sidebar.scss"; import "./sidebar.scss";
@ -29,7 +28,7 @@ export function HowLongToBeatSection({
if (!howLongToBeatData && !isLoading) return null; if (!howLongToBeatData && !isLoading) return null;
return ( return (
<SkeletonTheme baseColor={vars.color.background} highlightColor="#444"> <SkeletonTheme baseColor="#1c1c1c" highlightColor="#444">
<SidebarSection title="HowLongToBeat"> <SidebarSection title="HowLongToBeat">
<ul className="how-long-to-beat__categories-list"> <ul className="how-long-to-beat__categories-list">
{howLongToBeatData {howLongToBeatData

View File

@ -11,7 +11,6 @@ import flameIconStatic from "@renderer/assets/icons/flame-static.png";
import flameIconAnimated from "@renderer/assets/icons/flame-animated.gif"; import flameIconAnimated from "@renderer/assets/icons/flame-animated.gif";
import starsIconAnimated from "@renderer/assets/icons/stars-animated.gif"; import starsIconAnimated from "@renderer/assets/icons/stars-animated.gif";
import { vars } from "@renderer/theme.css";
import { buildGameDetailsPath } from "@renderer/helpers"; import { buildGameDetailsPath } from "@renderer/helpers";
import { CatalogueCategory } from "@shared"; import { CatalogueCategory } from "@shared";
import "./home.scss"; import "./home.scss";
@ -94,7 +93,7 @@ export default function Home() {
}; };
return ( return (
<SkeletonTheme baseColor={vars.color.background} highlightColor="#444"> <SkeletonTheme baseColor="#1c1c1c" highlightColor="#444">
<section className="home__content"> <section className="home__content">
<h2>{t("featured")}</h2> <h2>{t("featured")}</h2>

View File

@ -1,30 +0,0 @@
import { createGlobalTheme } from "@vanilla-extract/css";
export const SPACING_UNIT = 8;
export const vars = createGlobalTheme(":root", {
color: {
background: "#1c1c1c",
darkBackground: "#151515",
muted: "#c0c1c7",
body: "#8e919b",
border: "rgba(255, 255, 255, 0.15)",
success: "#1c9749",
danger: "#e11d48",
warning: "#ffc107",
},
opacity: {
disabled: "0.5",
active: "0.7",
},
size: {
body: "14px",
small: "12px",
},
zIndex: {
toast: "5",
bottomPanel: "3",
titleBar: "4",
backdrop: "4",
},
});