Merge branch 'main' into feat/improving-sources

This commit is contained in:
Chubby Granny Chaser
2025-10-26 21:42:58 +00:00
committed by GitHub
24 changed files with 2802 additions and 1103 deletions

View File

@@ -11,7 +11,6 @@
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.05);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
margin-bottom: calc(globals.$spacing-unit * 1.5);
&__info {
display: flex;

View File

@@ -2,7 +2,7 @@
.gallery-slider {
&__container {
padding: calc(globals.$spacing-unit * 1.5) calc(globals.$spacing-unit * 1);
padding: calc(globals.$spacing-unit * 1.5) 0;
width: 100%;
display: flex;
flex-direction: column;

View File

@@ -1,4 +1,4 @@
import { useContext, useEffect, useMemo, useRef, useState } from "react";
import { useContext, useEffect, useMemo, useState } from "react";
import { PencilIcon } from "@primer/octicons-react";
import { useTranslation } from "react-i18next";
@@ -17,6 +17,7 @@ import cloudIconAnimated from "@renderer/assets/icons/cloud-animated.gif";
import { useUserDetails, useLibrary } from "@renderer/hooks";
import { useSubscription } from "@renderer/hooks/use-subscription";
import "./game-details.scss";
import "./hero.scss";
const processMediaElements = (document: Document) => {
const $images = Array.from(document.querySelectorAll("img"));
@@ -53,8 +54,6 @@ const getImageWithCustomPriority = (
};
export function GameDetailsContent() {
const heroRef = useRef<HTMLDivElement | null>(null);
const { t } = useTranslation("game_details");
const {
@@ -152,18 +151,12 @@ export function GameDetailsContent() {
className={`game-details__wrapper ${hasNSFWContentBlocked ? "game-details__wrapper--blurred" : ""}`}
>
<section className="game-details__container">
<div ref={heroRef} className="game-details__hero">
<div className="game-details__hero">
<img
src={heroImage}
className="game-details__hero-image"
alt={game?.title}
/>
<div
className="game-details__hero-backdrop"
style={{
flex: 1,
}}
/>
<div
className="game-details__hero-logo-backdrop"
@@ -202,11 +195,13 @@ export function GameDetailsContent() {
)}
</div>
</div>
<div className="game-details__hero-panel">
<HeroPanel />
</div>
</div>
</div>
<HeroPanel />
<div className="game-details__description-container">
<div className="game-details__description-content">
<DescriptionHeader />

View File

@@ -1,63 +1,170 @@
import Skeleton from "react-loading-skeleton";
import { Button } from "@renderer/components";
import { useTranslation } from "react-i18next";
import "./game-details.scss";
import "react-loading-skeleton/dist/skeleton.css";
export function GameDetailsSkeleton() {
const { t } = useTranslation("game_details");
return (
<div className="game-details__container">
<div className="game-details__hero">
<Skeleton className="game-details__hero-image-skeleton" />
</div>
<div className="game-details__hero-panel-skeleton">
<section className="description-header__info">
<Skeleton width={155} />
<Skeleton width={135} />
</section>
</div>
<div className="game-details__description-container">
<div className="game-details__description-content">
<div className="description-header">
<section className="description-header__info">
<Skeleton width={145} />
<Skeleton width={150} />
</section>
<div className="game-details__wrapper game-details__skeleton">
<section className="game-details__container">
<div className="game-details__hero">
<Skeleton
height={350}
style={{
borderRadius: "0px 0px 8px 8px",
position: "absolute",
width: "100%",
zIndex: 0,
}}
/>
<div className="game-details__hero-logo-backdrop">
<div className="game-details__hero-content">
<div className="game-details__game-logo" />
<div className="game-details__hero-buttons game-details__hero-buttons--right" />
</div>
<div className="game-details__hero-panel">
<div className="hero-panel__container">
<div className="hero-panel">
<div className="hero-panel__content">
<Skeleton height={16} width={150} />
<Skeleton height={16} width={120} />
</div>
<div className="hero-panel__actions" style={{ gap: "16px" }}>
<Skeleton
height={36}
width={36}
style={{ borderRadius: "6px" }}
/>
<Skeleton
height={36}
width={36}
style={{ borderRadius: "6px" }}
/>
<Skeleton
height={36}
width={100}
style={{ borderRadius: "6px" }}
/>
</div>
</div>
</div>
</div>
</div>
<div className="game-details__description-skeleton">
{Array.from({ length: 3 }).map((_, index) => (
<Skeleton key={index} />
))}
<Skeleton className="game-details__hero-image-skeleton" />
{Array.from({ length: 2 }).map((_, index) => (
<Skeleton key={index} />
))}
<Skeleton className="game-details__hero-image-skeleton" />
<Skeleton />
</div>
<div className="game-details__description-container">
<div className="game-details__description-content">
<div className="description-header">
<section className="description-header__info">
<Skeleton height={16} width={200} />
<Skeleton height={16} width={150} />
</section>
</div>
<div style={{ marginBottom: "24px" }}>
<Skeleton
height={200}
width="100%"
style={{ borderRadius: "8px" }}
/>
</div>
<div className="game-details__description">
<Skeleton count={8} height={22} style={{ marginBottom: "8px" }} />
<Skeleton height={22} width="60%" />
</div>
<Skeleton
width={120}
height={36}
className="game-details__description-toggle"
width={100}
style={{
borderRadius: "4px",
marginTop: "24px",
alignSelf: "center",
}}
/>
<div style={{ marginTop: "48px" }} />
</div>
<aside className="content-sidebar">
<div className="sidebar-section">
<div
className="sidebar-section__button"
style={{ pointerEvents: "none" }}
>
<Skeleton height={16} width={16} />
<Skeleton height={16} width={60} />
</div>
<div className="sidebar-section__content">
<div className="stats__section">
<div className="stats__category">
<div className="stats__category-title">
<Skeleton height={14} width={14} />
<Skeleton height={14} width={80} />
</div>
<Skeleton height={14} width={40} />
</div>
<div className="stats__category">
<div className="stats__category-title">
<Skeleton height={14} width={14} />
<Skeleton height={14} width={70} />
</div>
<Skeleton height={14} width={35} />
</div>
<div className="stats__category">
<div className="stats__category-title">
<Skeleton height={14} width={14} />
<Skeleton height={14} width={60} />
</div>
<Skeleton height={14} width={30} />
</div>
</div>
</div>
</div>
<div className="sidebar-section">
<div
className="sidebar-section__button"
style={{ pointerEvents: "none" }}
>
<Skeleton height={16} width={16} />
<Skeleton height={16} width={120} />
</div>
<div className="sidebar-section__content">
<ul className="list">
{Array.from({ length: 4 }).map((_, index) => (
<li key={index}>
<div
className="list__item"
style={{ pointerEvents: "none" }}
>
<Skeleton
height={54}
width={54}
style={{ borderRadius: "4px" }}
/>
<div>
<Skeleton
height={14}
width={120}
style={{ marginBottom: "4px" }}
/>
<Skeleton height={12} width={80} />
</div>
</div>
</li>
))}
</ul>
</div>
</div>
</aside>
</div>
<div className="content-sidebar">
<div className="requirement__button-container">
<Button className="requirement__button" theme="primary" disabled>
{t("minimum")}
</Button>
<Button className="requirement__button" theme="outline" disabled>
{t("recommended")}
</Button>
</div>
<div className="requirement__details-skeleton">
{Array.from({ length: 6 }).map((_, index) => (
<Skeleton key={index} height={20} />
))}
</div>
</div>
</div>
</section>
</div>
);
}

View File

@@ -1,19 +1,5 @@
@use "../../scss/globals.scss";
$hero-height: 300px;
@keyframes slide-in {
0% {
transform: translateY(calc(40px + globals.$spacing-unit * 2));
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
.game-details {
&__wrapper {
display: flex;
@@ -27,617 +13,6 @@ $hero-height: 300px;
}
}
&__review-form {
display: flex;
flex-direction: column;
gap: 16px;
margin-bottom: 24px;
}
&__review-form-controls {
display: flex;
gap: calc(globals.$spacing-unit * 2);
align-items: flex-end;
flex-wrap: wrap;
@media (max-width: 768px) {
flex-direction: column;
align-items: stretch;
gap: calc(globals.$spacing-unit * 1.5);
}
}
&__review-form-bottom {
display: flex;
justify-content: space-between;
align-items: center;
gap: 16px;
flex-wrap: wrap;
}
&__review-message {
padding: calc(globals.$spacing-unit * 1);
border-radius: 4px;
font-size: globals.$small-font-size;
font-weight: 500;
margin-top: calc(globals.$spacing-unit * 1);
border: 1px solid;
&--success {
background: rgba(34, 197, 94, 0.1);
color: #86efac;
border-color: rgba(34, 197, 94, 0.3);
}
&--error {
background: rgba(239, 68, 68, 0.1);
color: #fca5a5;
border-color: rgba(239, 68, 68, 0.3);
}
}
&__review-score-container {
display: flex;
align-items: center;
gap: 4px;
}
&__review-score-label {
font-size: 14px;
color: #ffffff;
font-weight: 500;
}
&__review-score-select {
background-color: #2a2a2a;
border: 1px solid #3a3a3a;
border-radius: 4px;
color: #ffffff;
padding: 6px 12px;
font-size: 14px;
cursor: pointer;
transition:
border-color 0.2s ease,
background-color 0.2s ease;
&:focus {
outline: none;
}
&--red {
border-color: #e74c3c;
background-color: rgba(231, 76, 60, 0.1);
}
&--yellow {
border-color: #f39c12;
background-color: rgba(243, 156, 18, 0.1);
}
&--green {
border-color: #27ae60;
background-color: rgba(39, 174, 96, 0.1);
}
option {
background-color: #2a2a2a;
color: #ffffff;
}
}
&__star-rating {
display: flex;
align-items: center;
gap: 2px;
}
&__star {
background: none;
border: none;
color: #666666;
cursor: pointer;
padding: 2px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
&:hover {
color: #ffffff;
background-color: rgba(255, 255, 255, 0.1);
transform: scale(1.1);
}
&--filled {
color: #ffffff;
&.game-details__review-score-select--red {
color: #e74c3c;
}
&.game-details__review-score-select--yellow {
color: #f39c12;
}
&.game-details__review-score-select--green {
color: #27ae60;
}
}
&--empty {
color: #666666;
&:hover {
color: #ffffff;
}
}
svg {
fill: currentColor;
}
}
&__reviews-sort {
display: flex;
flex-direction: column;
gap: calc(globals.$spacing-unit * 0.75);
min-width: 150px;
}
&__reviews-sort-label {
display: block;
font-size: globals.$body-font-size;
color: globals.$body-color;
}
&__reviews-sort-select {
background-color: rgba(255, 255, 255, 0.05);
border: 1px solid globals.$border-color;
border-radius: 4px;
padding: calc(globals.$spacing-unit * 0.75) calc(globals.$spacing-unit * 1);
color: globals.$body-color;
font-size: globals.$body-font-size;
font-family: inherit;
cursor: pointer;
transition:
border-color 0.2s ease,
background-color 0.2s ease;
&:focus {
outline: none;
background-color: rgba(255, 255, 255, 0.08);
border-color: globals.$brand-teal;
}
&:hover {
border-color: rgba(255, 255, 255, 0.15);
}
option {
background-color: globals.$dark-background-color;
color: globals.$body-color;
}
}
&__reviews-list {
margin-top: calc(globals.$spacing-unit * 3);
}
&__reviews-container {
display: flex;
flex-direction: column;
gap: calc(globals.$spacing-unit * 4);
}
&__reviews-separator {
height: 1px;
background: rgba(255, 255, 255, 0.1);
margin: calc(globals.$spacing-unit * 3) 0;
width: 100%;
}
&__reviews-list-header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: calc(globals.$spacing-unit * 1);
}
&__reviews-empty {
text-align: center;
padding: calc(globals.$spacing-unit * 4) calc(globals.$spacing-unit * 2);
margin-bottom: calc(globals.$spacing-unit * 2);
}
&__reviews-empty-icon {
font-size: 48px;
margin-bottom: calc(globals.$spacing-unit * 2);
color: rgba(255, 255, 255, 0.6);
}
&__reviews-empty-title {
color: rgba(255, 255, 255, 0.9);
font-weight: 600;
margin: 0 0 calc(globals.$spacing-unit * 1) 0;
}
&__reviews-empty-message {
color: rgba(255, 255, 255, 0.6);
font-size: globals.$small-font-size;
margin: 0;
line-height: 1.4;
}
&__review-item {
overflow: hidden;
word-wrap: break-word;
}
&__review-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: calc(globals.$spacing-unit * 1.5);
}
&__review-user {
display: flex;
align-items: center;
gap: calc(globals.$spacing-unit * 1);
}
&__review-user-info {
display: flex;
flex-direction: column;
gap: calc(globals.$spacing-unit * 0.25);
}
&__review-display-name {
color: rgba(255, 255, 255, 0.9);
font-size: globals.$small-font-size;
font-weight: 600;
display: inline-flex;
&--clickable {
cursor: pointer;
transition: color 0.2s ease;
&:hover {
text-decoration: underline;
}
}
}
&__review-actions {
margin-top: 12px;
padding-top: 8px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
justify-content: space-between;
align-items: center;
}
&__review-votes {
display: flex;
gap: 12px;
}
&__vote-button {
display: flex;
align-items: center;
gap: 6px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 6px;
padding: 6px 12px;
color: #ccc;
font-size: 14px;
cursor: pointer;
transition: all 0.2s ease;
&:hover {
background: rgba(255, 255, 255, 0.1);
border-color: rgba(255, 255, 255, 0.2);
color: #ffffff;
}
&--active {
&.game-details__vote-button--upvote {
svg {
fill: white;
}
}
&.game-details__vote-button--downvote {
svg {
fill: white;
}
}
}
span {
font-weight: 500;
display: inline-block;
min-width: 1ch;
overflow: hidden;
}
}
&__delete-review-button {
display: flex;
align-items: center;
justify-content: center;
background: rgba(244, 67, 54, 0.1);
border: 1px solid rgba(244, 67, 54, 0.3);
border-radius: 6px;
padding: 6px;
color: #f44336;
cursor: pointer;
transition: all 0.2s ease;
gap: 6px;
&:hover {
background: rgba(244, 67, 54, 0.2);
border-color: #f44336;
color: #ff5722;
}
}
&__blocked-review-simple {
color: rgba(255, 255, 255, 0.6);
font-size: globals.$small-font-size;
display: flex;
align-items: center;
gap: calc(globals.$spacing-unit * 0.5);
}
&__blocked-review-show-link {
background: none;
border: none;
color: #ffc107;
font-size: globals.$small-font-size;
cursor: pointer;
text-decoration: underline;
padding: 0;
transition: color 0.2s ease;
&:hover {
color: #ffeb3b;
}
}
&__blocked-review-hide-link {
background: none;
border: none;
color: rgba(255, 255, 255, 0.5);
font-size: globals.$small-font-size;
cursor: pointer;
text-decoration: underline;
padding: 0;
transition: color 0.2s ease;
&:hover {
color: rgba(255, 255, 255, 0.8);
}
}
&__review-score-stars {
display: flex;
align-items: center;
gap: 2px;
}
&__review-star {
color: #666666;
transition: color 0.2s ease;
cursor: default;
&--filled {
color: #ffffff;
&.game-details__review-score--red {
color: #fca5a5;
}
&.game-details__review-score--yellow {
color: #fcd34d;
}
&.game-details__review-score--green {
color: #86efac;
}
}
&--empty {
color: #666666;
}
svg {
fill: currentColor;
}
}
&__review-date {
display: flex;
align-items: center;
gap: 4px;
color: rgba(255, 255, 255, 0.6);
font-size: globals.$small-font-size;
}
&__review-content {
color: globals.$body-color;
line-height: 1.5;
word-wrap: break-word;
word-break: break-word;
overflow-wrap: break-word;
white-space: pre-wrap;
max-width: 100%;
}
&__reviews-loading {
text-align: center;
color: rgba(255, 255, 255, 0.6);
padding: calc(globals.$spacing-unit * 2);
}
&__load-more-reviews {
background: rgba(255, 255, 255, 0.05);
border: 1px solid globals.$border-color;
color: globals.$body-color;
padding: calc(globals.$spacing-unit * 1) calc(globals.$spacing-unit * 2);
border-radius: 4px;
cursor: pointer;
font-size: globals.$body-font-size;
font-family: inherit;
transition: all 0.2s ease;
width: 100%;
margin-top: calc(globals.$spacing-unit * 2);
&:hover {
background-color: rgba(255, 255, 255, 0.1);
border-color: globals.$brand-teal;
}
}
&__hero {
width: 100%;
height: $hero-height;
min-height: $hero-height;
display: flex;
flex-direction: column;
position: relative;
transition: all ease 0.2s;
@media (min-width: 1250px) {
height: 350px;
min-height: 350px;
}
}
&__hero-content {
padding: calc(globals.$spacing-unit * 1.5);
height: 100%;
width: 100%;
display: flex;
justify-content: space-between;
align-items: flex-end;
@media (min-width: 768px) {
padding: calc(globals.$spacing-unit * 2);
}
}
&__hero-buttons {
display: flex;
gap: globals.$spacing-unit;
align-items: center;
&--right {
margin-left: auto;
}
}
&__edit-custom-game-button {
padding: calc(globals.$spacing-unit * 1.5);
background-color: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(20px);
border-radius: 8px;
transition: all ease 0.2s;
cursor: pointer;
min-height: 40px;
min-width: 40px;
display: flex;
align-items: center;
justify-content: center;
color: globals.$muted-color;
border: solid 1px globals.$border-color;
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.8);
animation: slide-in 0.3s cubic-bezier(0.33, 1, 0.68, 1);
&:active {
opacity: 0.9;
}
&:hover {
background-color: rgba(0, 0, 0, 0.5);
color: globals.$body-color;
}
}
&__hero-logo-backdrop {
width: 100%;
height: 100%;
background: linear-gradient(0deg, rgba(0, 0, 0, 0.3) 60%, transparent 100%);
position: absolute;
display: flex;
flex-direction: column;
justify-content: space-between;
}
&__hero-image {
width: 100%;
height: calc($hero-height + 72px);
min-height: calc($hero-height + 72px);
object-fit: cover;
object-position: top;
transition: all ease 0.2s;
position: absolute;
z-index: 0;
@media (min-width: 1250px) {
object-position: center;
height: calc(350px + 72px);
min-height: calc(350px + 72px);
}
}
&__game-logo {
width: 200px;
align-self: flex-end;
@media (min-width: 768px) {
width: 250px;
}
@media (min-width: 1024px) {
width: 300px;
}
}
&__game-logo-text {
width: 200px;
align-self: flex-end;
font-size: 1.8rem;
font-weight: bold;
color: #ffffff;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
text-align: left;
line-height: 1.2;
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
@media (min-width: 768px) {
width: 250px;
font-size: 2.2rem;
}
@media (min-width: 1024px) {
width: 300px;
font-size: 2.5rem;
}
}
&__hero-image-skeleton {
height: 300px;
@media (min-width: 1250px) {
height: 350px;
}
}
&__container {
width: 100%;
height: 100%;
@@ -646,6 +21,7 @@ $hero-height: 300px;
z-index: 1;
}
// Description Section Styles
&__description-container {
display: flex;
width: 100%;
@@ -754,322 +130,51 @@ $hero-height: 300px;
}
}
&__description-skeleton {
display: flex;
flex-direction: column;
gap: globals.$spacing-unit;
padding: calc(globals.$spacing-unit * 2) calc(globals.$spacing-unit * 1.5);
width: 100%;
margin-left: auto;
margin-right: auto;
@media (min-width: 768px) {
padding: calc(globals.$spacing-unit * 2.5) calc(globals.$spacing-unit * 2);
// Skeleton-specific styles
&__skeleton {
.react-loading-skeleton {
background: linear-gradient(90deg, #1c1c1c 25%, #2a2a2a 50%, #1c1c1c 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
}
@media (min-width: 1024px) {
padding: calc(globals.$spacing-unit * 3) calc(globals.$spacing-unit * 2);
width: 80%;
// Ensure skeleton elements maintain proper spacing
.description-header {
margin-bottom: calc(globals.$spacing-unit * 1.5);
}
@media (min-width: 1280px) {
width: 60%;
line-height: 22px;
.content-sidebar {
min-width: 300px;
max-width: 300px;
}
@media (min-width: 1536px) {
width: 50%;
}
}
&__randomizer-button {
animation: slide-in 0.2s;
position: fixed;
bottom: calc(globals.$spacing-unit * 3);
right: calc(9px + globals.$spacing-unit * 2);
box-shadow: rgba(255, 255, 255, 0.1) 0px 0px 10px 1px;
border: solid 2px globals.$border-color;
z-index: 1;
background-color: globals.$background-color;
&:hover {
background-color: globals.$background-color;
box-shadow: rgba(255, 255, 255, 0.1) 0px 0px 15px 5px;
opacity: 1;
}
&:active {
transform: scale(0.98);
}
&:disabled {
box-shadow: none;
transform: none;
opacity: 0.8;
background-color: globals.$background-color;
}
}
&__hero-panel-skeleton {
width: 100%;
padding: calc(globals.$spacing-unit * 2);
display: flex;
align-items: center;
background-color: globals.$background-color;
height: 72px;
border-bottom: solid 1px globals.$border-color;
}
&__cloud-sync-button {
padding: calc(globals.$spacing-unit * 1.5) calc(globals.$spacing-unit * 2);
background-color: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(20px);
border-radius: 8px;
transition: all ease 0.2s;
cursor: pointer;
min-height: 40px;
display: flex;
align-items: center;
justify-content: center;
gap: globals.$spacing-unit;
color: globals.$muted-color;
font-size: globals.$small-font-size;
border: solid 1px globals.$border-color;
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.8);
animation: slide-in 0.3s cubic-bezier(0.33, 1, 0.68, 1);
&:active {
opacity: 0.9;
}
&:disabled {
opacity: globals.$disabled-opacity;
cursor: not-allowed;
}
&:hover {
background-color: rgba(0, 0, 0, 0.5);
}
}
&__stars-icon-container {
width: 16px;
height: 16px;
position: relative;
}
&__stars-icon {
width: 70px;
position: absolute;
top: -28px;
left: -27px;
}
&__cloud-icon-container {
width: 20px;
height: 16px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
&__cloud-icon {
width: 26px;
position: absolute;
top: -3px;
}
&__hero-backdrop {
flex: 1;
transition: opacity 0.2s ease;
}
&__reviews-section {
margin-top: calc(globals.$spacing-unit * 3);
padding-top: calc(globals.$spacing-unit * 3);
border-top: 1px solid rgba(255, 255, 255, 0.1);
width: 100%;
margin-left: auto;
margin-right: auto;
@media (min-width: 1280px) {
width: 60%;
}
@media (min-width: 1536px) {
width: 50%;
}
}
&__reviews-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: calc(globals.$spacing-unit * 2);
@media (max-width: 768px) {
// Hero panel skeleton spacing
.hero-panel__content {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: calc(globals.$spacing-unit * 1.5);
}
}
&__reviews-title {
font-size: 1.25rem;
font-weight: 600;
color: globals.$muted-color;
margin: 0;
}
&__reviews-title-group {
display: flex;
align-items: center;
gap: calc(globals.$spacing-unit);
flex: 1;
}
&__reviews-badge {
background-color: rgba(255, 255, 255, 0.1);
color: rgba(255, 255, 255, 0.7);
padding: 4px 8px;
border-radius: 6px;
font-size: 12px;
font-weight: 600;
min-width: 24px;
text-align: center;
flex-shrink: 0;
}
&__leave-review-cta {
display: flex;
align-items: center;
gap: calc(globals.$spacing-unit * 0.5);
padding: calc(globals.$spacing-unit * 0.75)
calc(globals.$spacing-unit * 1.5);
background: linear-gradient(
135deg,
globals.$brand-teal,
globals.$brand-blue
);
color: white;
border: none;
border-radius: 8px;
font-size: 0.9rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
margin-bottom: calc(globals.$spacing-unit);
&:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(globals.$brand-teal, 0.3);
gap: calc(globals.$spacing-unit * 0.5);
}
&:active {
transform: translateY(0);
// Review items skeleton spacing
.review-item-skeleton {
border: 1px solid globals.$border-color;
border-radius: 8px;
padding: calc(globals.$spacing-unit * 1);
margin-bottom: calc(globals.$spacing-unit * 1);
}
svg {
flex-shrink: 0;
}
}
&__review-input-container {
display: flex;
flex-direction: column;
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
background-color: globals.$dark-background-color;
overflow: hidden;
}
&__review-input-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
background-color: globals.$background-color;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
&__review-editor-toolbar {
display: flex;
gap: 4px;
}
&__editor-button {
background: none;
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 4px;
color: #ffffff;
padding: 4px 8px;
cursor: pointer;
font-size: 12px;
transition: all 0.2s ease;
&:hover {
background-color: rgba(255, 255, 255, 0.08);
border-color: rgba(255, 255, 255, 0.2);
}
&.is-active {
background-color: globals.$brand-blue;
border-color: globals.$brand-blue;
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
&__review-char-counter {
font-size: 12px;
color: #888888;
.over-limit {
color: #ff6b6b;
}
}
&__review-input {
min-height: 120px;
padding: 12px;
cursor: text;
.ProseMirror {
outline: none;
color: #ffffff;
font-size: 14px;
line-height: 1.5;
min-height: 96px; // 120px - 24px padding
width: 100%;
cursor: text;
&:focus {
outline: none;
}
p {
margin: 0 0 8px 0;
&:last-child {
margin-bottom: 0;
}
}
strong {
font-weight: bold;
}
em {
font-style: italic;
}
u {
text-decoration: underline;
}
// Sidebar section spacing
.sidebar-section-skeleton {
margin-bottom: calc(globals.$spacing-unit * 1.5);
}
}
}
@keyframes skeleton-loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}

View File

@@ -25,6 +25,7 @@ import { Downloader, getDownloadersForUri } from "@shared";
import { CloudSyncModal } from "./cloud-sync-modal/cloud-sync-modal";
import { CloudSyncFilesModal } from "./cloud-sync-files-modal/cloud-sync-files-modal";
import "./game-details.scss";
import "./hero.scss";
export default function GameDetails() {
const [randomGame, setRandomGame] = useState<Steam250Game | null>(null);

View File

@@ -0,0 +1,116 @@
@use "../../scss/globals.scss";
.game-details {
&__reviews-section {
margin-top: calc(globals.$spacing-unit * 3);
padding-top: calc(globals.$spacing-unit * 3);
border-top: 1px solid rgba(255, 255, 255, 0.1);
width: 100%;
margin-left: auto;
margin-right: auto;
@media (min-width: 1280px) {
width: 60%;
}
@media (min-width: 1536px) {
width: 50%;
}
}
&__reviews-title {
font-size: 1.25rem;
font-weight: 600;
color: globals.$muted-color;
margin: 0;
}
&__reviews-title-group {
display: flex;
align-items: center;
gap: calc(globals.$spacing-unit);
flex: 1;
}
&__reviews-badge {
background-color: rgba(255, 255, 255, 0.1);
color: rgba(255, 255, 255, 0.7);
padding: 4px 8px;
border-radius: 6px;
font-size: 12px;
font-weight: 600;
min-width: 24px;
text-align: center;
flex-shrink: 0;
}
&__reviews-container {
display: flex;
flex-direction: column;
gap: calc(globals.$spacing-unit * 4);
}
&__reviews-separator {
height: 1px;
background: rgba(255, 255, 255, 0.1);
margin: calc(globals.$spacing-unit * 3) 0;
width: 100%;
}
&__reviews-list-header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: calc(globals.$spacing-unit * 1);
}
&__reviews-empty {
text-align: center;
padding: calc(globals.$spacing-unit * 4) calc(globals.$spacing-unit * 2);
margin-bottom: calc(globals.$spacing-unit * 2);
}
&__reviews-empty-icon {
font-size: 48px;
margin-bottom: calc(globals.$spacing-unit * 2);
color: rgba(255, 255, 255, 0.6);
}
&__reviews-empty-title {
color: rgba(255, 255, 255, 0.9);
font-weight: 600;
margin: 0 0 calc(globals.$spacing-unit * 1) 0;
}
&__reviews-empty-message {
color: rgba(255, 255, 255, 0.6);
font-size: globals.$small-font-size;
margin: 0;
line-height: 1.4;
}
&__reviews-loading {
text-align: center;
color: rgba(255, 255, 255, 0.6);
padding: calc(globals.$spacing-unit * 2);
}
&__load-more-reviews {
background: rgba(255, 255, 255, 0.05);
border: 1px solid globals.$border-color;
color: globals.$body-color;
padding: calc(globals.$spacing-unit * 1) calc(globals.$spacing-unit * 2);
border-radius: 4px;
cursor: pointer;
font-size: globals.$body-font-size;
font-family: inherit;
transition: all 0.2s ease;
width: 100%;
margin-top: calc(globals.$spacing-unit * 2);
&:hover {
background-color: rgba(255, 255, 255, 0.1);
border-color: globals.$brand-teal;
}
}
}

View File

@@ -9,6 +9,7 @@ import { ReviewForm } from "./review-form";
import { ReviewItem } from "./review-item";
import { ReviewSortOptions } from "./review-sort-options";
import { ReviewPromptBanner } from "./review-prompt-banner";
import "./game-reviews.scss";
import { useToast } from "@renderer/hooks";
type ReviewSortOption =
@@ -465,84 +466,82 @@ export function GameReviews({
</>
)}
<div className="game-details__reviews-list">
<div className="game-details__reviews-list-header">
<div className="game-details__reviews-title-group">
<h3 className="game-details__reviews-title">{t("reviews")}</h3>
<span className="game-details__reviews-badge">
{totalReviewCount}
</span>
</div>
<div className="game-details__reviews-list-header">
<div className="game-details__reviews-title-group">
<h3 className="game-details__reviews-title">{t("reviews")}</h3>
<span className="game-details__reviews-badge">
{totalReviewCount}
</span>
</div>
<ReviewSortOptions
sortBy={reviewsSortBy}
onSortChange={handleSortChange}
/>
{reviewsLoading && reviews.length === 0 && (
<div className="game-details__reviews-loading">
{t("loading_reviews")}
</div>
)}
{!reviewsLoading && reviews.length === 0 && (
<div className="game-details__reviews-empty">
<div className="game-details__reviews-empty-icon">
<NoteIcon size={48} />
</div>
<h4 className="game-details__reviews-empty-title">
{t("no_reviews_yet")}
</h4>
<p className="game-details__reviews-empty-message">
{t("be_first_to_review")}
</p>
</div>
)}
<div
className="game-details__reviews-container"
style={{
opacity: reviewsLoading && reviews.length > 0 ? 0.5 : 1,
transition: "opacity 0.2s ease",
}}
>
{reviews.map((review) => (
<ReviewItem
key={review.id}
review={review}
userDetailsId={userDetailsId}
isBlocked={review.isBlocked}
isVisible={visibleBlockedReviews.has(review.id)}
isVoting={votingReviews.has(review.id)}
previousVotes={
previousVotesRef.current.get(review.id) || {
upvotes: 0,
downvotes: 0,
}
}
onVote={handleVoteReview}
onDelete={handleDeleteReview}
onToggleVisibility={toggleBlockedReview}
onAnimationComplete={handleVoteAnimationComplete}
/>
))}
</div>
{hasMoreReviews && !reviewsLoading && (
<button
className="game-details__load-more-reviews"
onClick={loadMoreReviews}
>
{t("load_more_reviews")}
</button>
)}
{reviewsLoading && reviews.length > 0 && (
<div className="game-details__reviews-loading">
{t("loading_more_reviews")}
</div>
)}
</div>
<ReviewSortOptions
sortBy={reviewsSortBy}
onSortChange={handleSortChange}
/>
{reviewsLoading && reviews.length === 0 && (
<div className="game-details__reviews-loading">
{t("loading_reviews")}
</div>
)}
{!reviewsLoading && reviews.length === 0 && (
<div className="game-details__reviews-empty">
<div className="game-details__reviews-empty-icon">
<NoteIcon size={48} />
</div>
<h4 className="game-details__reviews-empty-title">
{t("no_reviews_yet")}
</h4>
<p className="game-details__reviews-empty-message">
{t("be_first_to_review")}
</p>
</div>
)}
<div
className="game-details__reviews-container"
style={{
opacity: reviewsLoading && reviews.length > 0 ? 0.5 : 1,
transition: "opacity 0.2s ease",
}}
>
{reviews.map((review) => (
<ReviewItem
key={review.id}
review={review}
userDetailsId={userDetailsId}
isBlocked={review.isBlocked}
isVisible={visibleBlockedReviews.has(review.id)}
isVoting={votingReviews.has(review.id)}
previousVotes={
previousVotesRef.current.get(review.id) || {
upvotes: 0,
downvotes: 0,
}
}
onVote={handleVoteReview}
onDelete={handleDeleteReview}
onToggleVisibility={toggleBlockedReview}
onAnimationComplete={handleVoteAnimationComplete}
/>
))}
</div>
{hasMoreReviews && !reviewsLoading && (
<button
className="game-details__load-more-reviews"
onClick={loadMoreReviews}
>
{t("load_more_reviews")}
</button>
)}
{reviewsLoading && reviews.length > 0 && (
<div className="game-details__reviews-loading">
{t("loading_more_reviews")}
</div>
)}
</div>
);
}

View File

@@ -0,0 +1,271 @@
@use "../../scss/globals.scss";
$hero-height: 350px;
@keyframes slide-in {
0% {
transform: translateY(calc(40px + globals.$spacing-unit * 2));
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
.game-details {
&__hero-panel {
padding: globals.$spacing-unit;
}
&__hero {
width: 100%;
height: $hero-height;
min-height: $hero-height;
display: flex;
flex-direction: column;
position: relative;
transition: all ease 0.2s;
@media (min-width: 1250px) {
height: 350px;
min-height: 350px;
}
}
&__hero-content {
padding: calc(globals.$spacing-unit * 1.5);
height: 100%;
width: 100%;
display: flex;
justify-content: space-between;
align-items: flex-end;
@media (min-width: 768px) {
padding: calc(globals.$spacing-unit * 2);
}
}
&__hero-buttons {
display: flex;
gap: globals.$spacing-unit;
align-items: center;
&--right {
margin-left: auto;
}
}
&__edit-custom-game-button {
padding: calc(globals.$spacing-unit * 1.5);
background-color: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(20px);
border-radius: 8px;
transition: all ease 0.2s;
cursor: pointer;
min-height: 40px;
min-width: 40px;
display: flex;
align-items: center;
justify-content: center;
color: globals.$muted-color;
border: solid 1px globals.$border-color;
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.8);
animation: slide-in 0.3s cubic-bezier(0.33, 1, 0.68, 1);
&:active {
opacity: 0.9;
}
&:hover {
background-color: rgba(0, 0, 0, 0.5);
color: globals.$body-color;
}
}
&__hero-logo-backdrop {
width: 100%;
height: 100%;
position: absolute;
display: flex;
flex-direction: column;
justify-content: space-between;
}
&__hero-image-wrapper {
position: absolute;
width: 100%;
height: 384px;
max-height: 384px;
overflow: hidden;
border-radius: 0px 0px 8px 8px;
z-index: 0;
&::after {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(
0deg,
rgba(0, 0, 0, 0.3) 60%,
transparent 100%
);
z-index: 1;
pointer-events: none;
border-radius: inherit;
}
@media (min-width: 1250px) {
height: calc(350px + 82px);
min-height: calc(350px + 84px);
}
}
&__hero-image {
width: 100%;
height: $hero-height;
min-height: $hero-height;
object-fit: cover;
object-position: top;
transition: all ease 0.2s;
position: absolute;
z-index: 0;
border-radius: 0px 0px 8px 8px;
@media (min-width: 1250px) {
object-position: center;
height: $hero-height;
min-height: $hero-height;
}
}
&__game-logo {
width: 200px;
align-self: flex-end;
@media (min-width: 768px) {
width: 250px;
}
@media (min-width: 1024px) {
width: 300px;
}
}
&__game-logo-text {
width: 200px;
align-self: flex-end;
font-size: 1.8rem;
font-weight: bold;
color: #ffffff;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
text-align: left;
line-height: 1.2;
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
@media (min-width: 768px) {
width: 250px;
font-size: 2.2rem;
}
@media (min-width: 1024px) {
width: 300px;
font-size: 2.5rem;
}
}
&__cloud-sync-button {
padding: calc(globals.$spacing-unit * 1.5) calc(globals.$spacing-unit * 2);
background-color: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(20px);
border-radius: 8px;
transition: all ease 0.2s;
cursor: pointer;
min-height: 40px;
display: flex;
align-items: center;
justify-content: center;
gap: globals.$spacing-unit;
color: globals.$muted-color;
font-size: globals.$small-font-size;
border: solid 1px globals.$border-color;
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.8);
animation: slide-in 0.3s cubic-bezier(0.33, 1, 0.68, 1);
&:active {
opacity: 0.9;
}
&:disabled {
opacity: globals.$disabled-opacity;
cursor: not-allowed;
}
&:hover {
background-color: rgba(0, 0, 0, 0.5);
}
}
&__cloud-icon-container {
width: 20px;
height: 16px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
&__cloud-icon {
width: 26px;
position: absolute;
top: -3px;
}
&__randomizer-button {
padding: calc(globals.$spacing-unit * 1.5);
background-color: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(20px);
border-radius: 8px;
transition: all ease 0.2s;
cursor: pointer;
min-height: 40px;
min-width: 40px;
display: flex;
align-items: center;
justify-content: center;
color: globals.$muted-color;
border: solid 1px globals.$border-color;
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.8);
animation: slide-in 0.3s cubic-bezier(0.33, 1, 0.68, 1);
&:active {
opacity: 0.9;
}
&:hover {
background-color: rgba(0, 0, 0, 0.5);
color: globals.$body-color;
}
}
&__stars-icon-container {
width: 20px;
height: 16px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
&__stars-icon {
width: 26px;
position: absolute;
top: -3px;
}
}

View File

@@ -18,6 +18,7 @@
top: 0;
z-index: 2;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
border-radius: 8px;
&--stuck {
background: rgba(0, 0, 0, 0.7);
@@ -29,7 +30,18 @@
&__content {
display: flex;
flex-direction: column;
gap: globals.$spacing-unit;
gap: calc(globals.$spacing-unit * 0.5);
p {
font-size: globals.$small-font-size;
color: globals.$muted-color;
font-weight: 400;
margin: 0;
&:first-child {
font-weight: 600;
}
}
}
&__actions {

View File

@@ -50,25 +50,29 @@ export function HeroPanel() {
game?.download?.status === "paused";
return (
<div className="hero-panel">
<div className="hero-panel__content">{getInfo()}</div>
<div className="hero-panel__actions">
<HeroPanelActions />
</div>
<div className="hero-panel__container">
<div className="hero-panel">
<div className="hero-panel__content">{getInfo()}</div>
<div className="hero-panel__actions">
<HeroPanelActions />
</div>
{showProgressBar && (
<progress
max={1}
value={
isGameDownloading ? lastPacket?.progress : game?.download?.progress
}
className={`hero-panel__progress-bar ${
game?.download?.status === "paused"
? "hero-panel__progress-bar--disabled"
: ""
}`}
/>
)}
{showProgressBar && (
<progress
max={1}
value={
isGameDownloading
? lastPacket?.progress
: game?.download?.progress
}
className={`hero-panel__progress-bar ${
game?.download?.status === "paused"
? "hero-panel__progress-bar--disabled"
: ""
}`}
/>
)}
</div>
</div>
);
}

View File

@@ -0,0 +1,232 @@
@use "../../scss/globals.scss";
.game-details {
&__reviews-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: calc(globals.$spacing-unit * 2);
@media (max-width: 768px) {
flex-direction: column;
align-items: flex-start;
gap: calc(globals.$spacing-unit * 1.5);
}
}
&__reviews-title {
font-size: 1.25rem;
font-weight: 600;
color: globals.$muted-color;
margin: 0;
}
&__review-form {
display: flex;
flex-direction: column;
gap: 16px;
margin-bottom: 24px;
}
&__review-form-bottom {
display: flex;
justify-content: space-between;
align-items: center;
gap: 16px;
flex-wrap: wrap;
}
&__review-score-container {
display: flex;
align-items: center;
gap: 4px;
}
&__review-score-select {
background-color: #2a2a2a;
border: 1px solid #3a3a3a;
border-radius: 4px;
color: #ffffff;
padding: 6px 12px;
font-size: 14px;
cursor: pointer;
transition:
border-color 0.2s ease,
background-color 0.2s ease;
&:focus {
outline: none;
}
&--red {
border-color: #e74c3c;
background-color: rgba(231, 76, 60, 0.1);
}
&--yellow {
border-color: #f39c12;
background-color: rgba(243, 156, 18, 0.1);
}
&--green {
border-color: #27ae60;
background-color: rgba(39, 174, 96, 0.1);
}
option {
background-color: #2a2a2a;
color: #ffffff;
}
}
&__star-rating {
display: flex;
align-items: center;
gap: 2px;
}
&__star {
background: none;
border: none;
color: #666666;
cursor: pointer;
padding: 2px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
&:hover {
color: #ffffff;
background-color: rgba(255, 255, 255, 0.1);
transform: scale(1.1);
}
&--filled {
color: #ffffff;
&.game-details__review-score-select--red {
color: #e74c3c;
}
&.game-details__review-score-select--yellow {
color: #f39c12;
}
&.game-details__review-score-select--green {
color: #27ae60;
}
}
&--empty {
color: #666666;
&:hover {
color: #ffffff;
}
}
svg {
fill: currentColor;
}
}
&__review-input-container {
display: flex;
flex-direction: column;
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
background-color: globals.$dark-background-color;
overflow: hidden;
}
&__review-input-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
background-color: globals.$background-color;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
&__review-editor-toolbar {
display: flex;
gap: 4px;
}
&__editor-button {
background: none;
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 4px;
color: #ffffff;
padding: 4px 8px;
cursor: pointer;
font-size: 12px;
transition: all 0.2s ease;
&:hover {
background-color: rgba(255, 255, 255, 0.08);
border-color: rgba(255, 255, 255, 0.2);
}
&.is-active {
background-color: globals.$brand-blue;
border-color: globals.$brand-blue;
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
&__review-char-counter {
font-size: 12px;
color: #888888;
.over-limit {
color: #ff6b6b;
}
}
&__review-input {
min-height: 120px;
padding: 12px;
cursor: text;
.ProseMirror {
outline: none;
color: #ffffff;
font-size: 14px;
line-height: 1.5;
min-height: 96px; // 120px - 24px padding
width: 100%;
cursor: text;
&:focus {
outline: none;
}
p {
margin: 0 0 8px 0;
&:last-child {
margin-bottom: 0;
}
}
strong {
font-weight: bold;
}
em {
font-style: italic;
}
u {
text-decoration: underline;
}
}
}
}

View File

@@ -2,6 +2,7 @@ import { Star } from "lucide-react";
import { useTranslation } from "react-i18next";
import { EditorContent, Editor } from "@tiptap/react";
import { Button } from "@renderer/components";
import "./review-form.scss";
interface ReviewFormProps {
editor: Editor | null;

View File

@@ -1,6 +1,213 @@
@use "../../scss/globals.scss";
.game-details {
&__review-item {
overflow: hidden;
word-wrap: break-word;
}
&__review-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: calc(globals.$spacing-unit * 1.5);
}
&__review-user {
display: flex;
align-items: center;
gap: calc(globals.$spacing-unit * 1);
}
&__review-user-info {
display: flex;
flex-direction: column;
gap: calc(globals.$spacing-unit * 0.25);
}
&__review-display-name {
color: rgba(255, 255, 255, 0.9);
font-size: globals.$small-font-size;
font-weight: 600;
display: inline-flex;
&--clickable {
cursor: pointer;
transition: color 0.2s ease;
&:hover {
text-decoration: underline;
}
}
}
&__review-actions {
margin-top: 12px;
padding-top: 8px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
justify-content: space-between;
align-items: center;
}
&__review-votes {
display: flex;
gap: 12px;
}
&__vote-button {
display: flex;
align-items: center;
gap: 6px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 6px;
padding: 6px 12px;
color: #ccc;
font-size: 14px;
cursor: pointer;
transition: all 0.2s ease;
&:hover {
background: rgba(255, 255, 255, 0.1);
border-color: rgba(255, 255, 255, 0.2);
color: #ffffff;
}
&--active {
&.game-details__vote-button--upvote {
svg {
fill: white;
}
}
&.game-details__vote-button--downvote {
svg {
fill: white;
}
}
}
span {
font-weight: 500;
display: inline-block;
min-width: 1ch;
overflow: hidden;
}
}
&__delete-review-button {
display: flex;
align-items: center;
justify-content: center;
background: rgba(244, 67, 54, 0.1);
border: 1px solid rgba(244, 67, 54, 0.3);
border-radius: 6px;
padding: 6px;
color: #f44336;
cursor: pointer;
transition: all 0.2s ease;
gap: 6px;
&:hover {
background: rgba(244, 67, 54, 0.2);
border-color: #f44336;
color: #ff5722;
}
}
&__blocked-review-simple {
color: rgba(255, 255, 255, 0.6);
font-size: globals.$small-font-size;
display: flex;
align-items: center;
gap: calc(globals.$spacing-unit * 0.5);
}
&__blocked-review-show-link {
background: none;
border: none;
color: #ffc107;
font-size: globals.$small-font-size;
cursor: pointer;
text-decoration: underline;
padding: 0;
transition: color 0.2s ease;
&:hover {
color: #ffeb3b;
}
}
&__blocked-review-hide-link {
background: none;
border: none;
color: rgba(255, 255, 255, 0.5);
font-size: globals.$small-font-size;
cursor: pointer;
text-decoration: underline;
padding: 0;
transition: color 0.2s ease;
&:hover {
color: rgba(255, 255, 255, 0.8);
}
}
&__review-score-stars {
display: flex;
align-items: center;
gap: 2px;
}
&__review-star {
color: #666666;
transition: color 0.2s ease;
cursor: default;
&--filled {
color: #ffffff;
&.game-details__review-score--red {
color: #fca5a5;
}
&.game-details__review-score--yellow {
color: #fcd34d;
}
&.game-details__review-score--green {
color: #86efac;
}
}
&--empty {
color: #666666;
}
svg {
fill: currentColor;
}
}
&__review-date {
display: flex;
align-items: center;
gap: 4px;
color: rgba(255, 255, 255, 0.6);
font-size: globals.$small-font-size;
}
&__review-content {
color: globals.$body-color;
line-height: 1.5;
word-wrap: break-word;
word-break: break-word;
overflow-wrap: break-word;
white-space: pre-wrap;
max-width: 100%;
}
&__review-translation-toggle {
display: inline-flex;
align-items: center;