Migrate some builds files to CSS modules

This commit is contained in:
Kalle 2025-06-14 09:38:29 +03:00
parent b4bd9aabca
commit fb51e3c463
24 changed files with 379 additions and 360 deletions

View File

@ -0,0 +1,34 @@
.container {
display: flex;
width: 100%;
flex-direction: column;
justify-content: center;
gap: var(--s-3);
}
.slots {
display: grid;
margin: 0 auto;
gap: var(--s-2);
grid-template-columns: repeat(4, max-content);
place-items: center;
}
.abilityButtons {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: var(--s-1);
touch-action: manipulation;
}
.abilityButton {
padding: var(--s-0-5);
border-color: var(--abilities-button-bg);
border-radius: 50%;
background-color: var(--abilities-button-bg);
}
.abilityButton.isDragging {
box-shadow: 0 0 100px inset rgb(255 255 255 / 25%);
}

View File

@ -7,6 +7,8 @@ import { abilityImageUrl } from "~/utils/urls";
import { Ability } from "./Ability";
import { Image } from "./Image";
import styles from "./AbilitiesSelector.module.css";
interface AbilitiesSelectorProps {
selectedAbilities: BuildAbilitiesTupleWithUnknown;
onChange: (newAbilities: BuildAbilitiesTupleWithUnknown) => void;
@ -79,8 +81,8 @@ export function AbilitiesSelector({
};
return (
<div className="ability-selector__container" data-testid="ability-selector">
<div className="ability-selector__slots">
<div className={styles.container} data-testid="ability-selector">
<div className={styles.slots}>
{selectedAbilities.map((row, rowI) =>
row.map((ability, abilityI) => (
<Ability
@ -99,12 +101,12 @@ export function AbilitiesSelector({
)),
)}
</div>
<div className="ability-selector__ability-buttons">
<div className={styles.abilityButtons}>
{abilities.map((ability) => (
<button
key={ability.name}
className={clsx("ability-selector__ability-button", {
"is-dragging": ability.name === draggingAbility?.name,
className={clsx(styles.abilityButton, {
[styles.isDragging]: ability.name === draggingAbility?.name,
})}
type="button"
onClick={() => onButtonClick(ability)}

View File

@ -0,0 +1,32 @@
.ability {
width: var(--ability-size);
height: var(--ability-size);
padding: 0;
border: 2px solid var(--theme-transparent);
border-radius: 50%;
border-right: 0;
border-bottom: 0;
background: var(--bg-ability);
background-size: 100%;
box-shadow: 0 0 0 1px var(--bg-ability);
transform: scale(1);
transition: all 0.1s ease;
user-select: none;
}
.isDragTarget {
background: var(--abilities-button-bg);
transform: scale(1.15);
}
.dragStarted:not(.dropAllowed) {
filter: grayscale(1);
opacity: 0.3;
pointer-events: none;
}
.readonly,
.readonly:active {
cursor: default;
transform: none;
}

View File

@ -5,6 +5,8 @@ import type { AbilityWithUnknown } from "~/modules/in-game-lists/types";
import { abilityImageUrl } from "~/utils/urls";
import { Image } from "./Image";
import styles from "./Ability.module.css";
const sizeMap = {
MAIN: 42,
SUB: 32,
@ -56,12 +58,12 @@ export function Ability({
return (
<AbilityTag
className={clsx(
"build__ability",
styles.ability,
{
"is-drag-target": isDragTarget,
"drag-started": dragStarted,
"drop-allowed": dropAllowed,
readonly,
[styles.isDragTarget]: isDragTarget,
[styles.dragStarted]: dragStarted,
[styles.dropAllowed]: dropAllowed,
[styles.readonly]: readonly,
},
className,
)}

View File

@ -0,0 +1,126 @@
.card {
position: relative;
display: flex;
flex-direction: column;
padding: var(--s-3);
border-radius: var(--rounded);
background-color: var(--bg-lighter);
gap: var(--s-3);
}
.private {
background-color: var(--bg-lighter-transparent);
}
.privateText {
display: flex;
justify-content: center;
font-weight: var(--semi-bold);
gap: var(--s-1);
}
.privateIcon {
width: 16px;
margin-block-end: var(--s-1);
stroke-width: 2px;
}
.title {
overflow: hidden;
height: 2.5rem;
font-size: var(--fonts-sm);
line-height: 1.25;
word-wrap: break-all;
}
.topRow {
display: flex;
justify-content: space-between;
}
.dateAuthorRow {
display: flex;
font-size: var(--fonts-xxs);
gap: var(--s-1);
}
.ownerLink {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.modes {
position: absolute;
right: 0;
display: flex;
min-width: max-content;
margin-top: -21px;
gap: var(--s-1);
}
.weapon {
position: relative;
padding: var(--s-0-5);
border-radius: 50%;
background-color: var(--bg-darker-very-transparent);
}
.top500 {
position: absolute;
top: -14px;
right: 8px;
}
.weaponText {
padding-left: var(--s-1);
color: var(--text-lighter);
font-size: var(--fonts-xxs);
font-weight: var(--semi-bold);
}
.weapons {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
gap: var(--s-1);
}
.gearAbilities {
display: grid;
gap: var(--s-2) var(--s-1);
grid-template-columns: repeat(5, max-content);
place-items: center;
}
.noGear {
grid-template-columns: repeat(4, max-content);
margin: 0 auto;
margin-top: 10px;
margin-bottom: 1rem;
row-gap: 28px;
}
.gear {
border-radius: 50%;
background-color: var(--bg-darker-very-transparent);
overflow: visible;
}
.bottomRow {
display: flex;
height: 100%;
align-items: flex-end;
justify-content: center;
gap: var(--s-4);
}
.icon {
width: 1.2rem;
height: 1.2rem;
}
.smallText {
font-size: var(--fonts-xxs) !important;
}

View File

@ -33,6 +33,8 @@ import { LockIcon } from "./icons/Lock";
import { SpeechBubbleIcon } from "./icons/SpeechBubble";
import { TrashIcon } from "./icons/Trash";
import styles from "./BuildCard.module.css";
interface BuildProps {
build: Pick<
Tables["Build"],
@ -92,13 +94,13 @@ export function BuildCard({
return (
<div
className={clsx("build", { build__private: build.private })}
className={clsx(styles.card, { [styles.private]: build.private })}
data-testid="build-card"
>
<div>
<div className="build__top-row">
<div className={styles.topRow}>
{modes && modes.length > 0 && (
<div className="build__modes">
<div className={styles.modes}>
{modes.map((mode) => (
<Image
key={mode}
@ -112,17 +114,14 @@ export function BuildCard({
))}
</div>
)}
<h2 className="build__title" data-testid="build-title">
<h2 className={styles.title} data-testid="build-title">
{title}
</h2>
</div>
<div className="build__date-author-row">
<div className={styles.dateAuthorRow}>
{owner && (
<>
<Link
to={userBuildsPage(owner)}
className="build__date-author-row__owner"
>
<Link to={userBuildsPage(owner)} className={styles.ownerLink}>
{owner.username}
</Link>
<div></div>
@ -136,8 +135,8 @@ export function BuildCard({
) : null}
<div className="stack horizontal sm">
{build.private ? (
<div className="build__private-text">
<LockIcon className="build__private-icon" />{" "}
<div className={styles.privateText}>
<LockIcon className={styles.privateIcon} />{" "}
{t("common:build.private")}
</div>
) : null}
@ -158,19 +157,19 @@ export function BuildCard({
</div>
</div>
</div>
<div className="build__weapons">
<div className={styles.weapons}>
{weapons.map((weapon) => (
<RoundWeaponImage key={weapon.weaponSplId} weapon={weapon} />
))}
{weapons.length === 1 && (
<div className="build__weapon-text">
<div className={styles.weaponText}>
{t(`weapons:MAIN_${weapons[0].weaponSplId}` as any)}
</div>
)}
</div>
<div
className={clsx("build__gear-abilities", {
"build__gear-abilities__no-gear": isNoGear,
className={clsx(styles.gearAbilities, {
[styles.noGear]: isNoGear,
})}
>
<AbilitiesRowWithGear
@ -189,7 +188,7 @@ export function BuildCard({
gearId={shoesGearSplId}
/>
</div>
<div className="build__bottom-row">
<div className={styles.bottomRow}>
<Link
to={analyzerPage({
weaponId: weapons[0].weaponSplId,
@ -198,7 +197,7 @@ export function BuildCard({
>
<Image
alt={t("common:pages.analyzer")}
className="build__icon"
className={styles.icon}
path={navIconUrl("analyzer")}
/>
</Link>
@ -208,7 +207,7 @@ export function BuildCard({
<SendouButton
variant="minimal"
icon={<SpeechBubbleIcon />}
className="build__small-text"
className={styles.smallText}
/>
}
>
@ -218,13 +217,13 @@ export function BuildCard({
{canEdit && (
<>
<LinkButton
className="build__small-text"
className={styles.smallText}
variant="minimal"
size="small"
to={`new?buildId=${id}&userId=${user!.id}`}
testId="edit-build"
>
<EditIcon className="build__icon" />
<EditIcon className={styles.icon} />
</LinkButton>
<FormWithConfirm
dialogHeading={t("builds:deleteConfirm", { title })}
@ -234,8 +233,8 @@ export function BuildCard({
]}
>
<SendouButton
icon={<TrashIcon className="build__icon" />}
className="build__small-text"
icon={<TrashIcon className={styles.icon} />}
className={styles.smallText}
variant="minimal-destructive"
type="submit"
/>
@ -259,10 +258,10 @@ function RoundWeaponImage({ weapon }: { weapon: BuildWeaponWithTop500Info }) {
const isTop500 = typeof maxPower === "number" && typeof minRank === "number";
return (
<div key={weaponSplId} className="build__weapon">
<div key={weaponSplId} className={styles.weapon}>
{isTop500 ? (
<Image
className="build__top500"
className={styles.top500}
path={navIconUrl("xsearch")}
alt=""
title={`Max X Power: ${maxPower} | Best Rank: ${minRank}`}
@ -307,7 +306,7 @@ function AbilitiesRowWithGear({
alt={translatedGearName}
title={translatedGearName}
path={gearImageUrl(gearType, gearId)}
className="build__gear"
className={styles.gear}
/>
) : null}
{abilities.map((ability, i) => (

View File

@ -109,8 +109,8 @@ function Association({
]}
>
<SendouButton
icon={<TrashIcon className="build__icon" />}
className="build__small-text"
icon={<TrashIcon className="small-icon" />}
className="small-text"
variant="minimal-destructive"
type="submit"
data-testid="delete-association"
@ -249,8 +249,8 @@ function AssociationMember({
]}
>
<SendouButton
icon={<TrashIcon className="build__icon" />}
className="build__small-text"
icon={<TrashIcon className="small-icon" />}
className="small-text"
variant="minimal-destructive"
type="submit"
/>

View File

@ -0,0 +1,37 @@
.filter {
display: flex;
flex-direction: column;
padding: var(--s-2-5);
border-radius: var(--rounded);
background-color: var(--bg-lighter);
gap: var(--s-2);
}
.filterMode {
gap: var(--s-6);
}
.filterDate {
display: flex;
align-items: center;
}
@media screen and (min-width: 750px) {
.filter {
flex-direction: row;
}
}
.abilityContainer {
display: flex;
width: 32px;
align-items: center;
}
.apSelect {
width: 75px;
}
.dateSelect {
width: 275px;
}

View File

@ -1,3 +1,4 @@
import clsx from "clsx";
import { useTranslation } from "react-i18next";
import { Ability } from "~/components/Ability";
import { ModeImage } from "~/components/Image";
@ -17,6 +18,8 @@ import type {
ModeBuildFilter,
} from "../builds-types";
import styles from "./FilterSection.module.css";
export function FilterSection({
number,
nthOfSame,
@ -74,8 +77,8 @@ function AbilityFilter({
const abilityObject = abilities.find((a) => a.name === filter.ability)!;
return (
<div className="build__filter">
<div className="build__filter__ability">
<div className={styles.filter}>
<div className={styles.abilityContainer}>
<Ability ability={filter.ability} size="TINY" />
</div>
<select
@ -125,7 +128,7 @@ function AbilityFilter({
{abilityObject.type === "STACKABLE" ? (
<div className="stack horizontal sm items-center">
<select
className="build__filter__ap-select"
className={styles.apSelect}
value={typeof filter.value === "number" ? filter.value : "0"}
onChange={(e) => onChange({ value: Number(e.target.value) })}
>
@ -156,7 +159,7 @@ function ModeFilter({
const inputId = (mode: ModeShort) => `${number}-${mode}`;
return (
<div className="build__filter build__filter__mode">
<div className={clsx(styles.filter, styles.filterMode)}>
{modesShort.map((mode) => {
return (
<div
@ -213,10 +216,10 @@ function DateFilter({
);
return (
<div className="build__filter build__filter__date">
<div className={clsx(styles.filter, styles.filterDate)}>
<label className="mb-0">{t("builds:filters.date.since")}</label>
<select
className="build__filter__date-select"
className={styles.dateSelect}
value={selectValue()}
data-testid="date-select"
onChange={(e) =>

View File

@ -0,0 +1,24 @@
.buildsContainer {
display: grid;
justify-content: center;
gap: var(--s-4) var(--s-3);
grid-template-columns: repeat(auto-fit, 240px);
}
.buildsButtons {
display: flex;
justify-content: space-between;
align-items: flex-start;
}
.buildsButtonsLink {
display: flex;
flex-direction: column;
gap: var(--s-2);
}
@media screen and (min-width: 480px) {
.buildsButtonsLink {
flex-direction: row;
}
}

View File

@ -45,6 +45,8 @@ import { FilterSection } from "../components/FilterSection";
import { loader } from "../loaders/builds.$slug.server";
export { loader };
import styles from "./builds.$slug.module.css";
const filterOutMeaninglessFilters = (
filter: Unpacked<BuildFiltersFromSearchParams>,
) => {
@ -165,7 +167,7 @@ export function BuildCards({
const user = useUser();
return (
<div className="builds-container">
<div className={styles.buildsContainer}>
{data.builds.map((build) => {
return (
<BuildCard
@ -277,7 +279,7 @@ export default function WeaponsBuildsPage() {
return (
<Main className="stack lg">
<div className="builds-buttons">
<div className={styles.buildsButtons}>
<SendouMenu
trigger={
<SendouButton
@ -315,7 +317,7 @@ export default function WeaponsBuildsPage() {
{t("builds:filters.type.date")}
</SendouMenuItem>
</SendouMenu>
<div className="builds-buttons__link">
<div className={styles.buildsButtonsLink}>
<LinkButton
to={weaponBuildStatsPage(data.slug)}
variant="outlined"

View File

@ -5,3 +5,46 @@
border-radius: var(--rounded);
background-color: var(--bg-lightest);
}
.topContainer {
display: flex;
align-items: center;
justify-content: flex-end;
}
.category {
display: flex;
flex-direction: column;
padding: var(--s-3);
border-radius: var(--rounded);
background-color: var(--bg-lighter);
font-size: var(--fonts-sm);
font-weight: 600;
gap: var(--s-4);
}
.categoryHeader {
display: flex;
align-items: center;
gap: var(--s-2-5);
}
.categoryWeapons {
display: flex;
flex-wrap: wrap;
gap: var(--s-3);
}
.categoryWeapon {
display: flex;
align-items: center;
color: var(--text);
font-size: var(--fonts-xs);
font-weight: var(--body);
gap: var(--s-1);
}
.categoryWeaponImg {
border-radius: var(--rounded);
background-color: var(--bg-darker);
}

View File

@ -23,8 +23,6 @@ import {
} from "~/utils/urls";
import { metaTags } from "../../../utils/remix";
import "~/styles/builds.css";
import styles from "./builds.module.css";
export const meta: MetaFunction = (args) => {
@ -62,8 +60,8 @@ export default function BuildsPage() {
</div>
) : null}
{weaponCategories.map((category) => (
<div key={category.name} className="builds__category">
<div className="builds__category__header">
<div key={category.name} className={styles.category}>
<div className={styles.categoryHeader}>
<Image
path={weaponCategoryUrl(category.name)}
width={40}
@ -72,7 +70,7 @@ export default function BuildsPage() {
/>
{t(`common:weapon.category.${category.name}`)}
</div>
<div className="builds__category__weapons">
<div className={styles.categoryWeapons}>
{(category.weaponIds as readonly MainWeaponId[])
.filter(weaponIdIsNotAlt)
.map((weaponId, i) => (
@ -83,11 +81,11 @@ export default function BuildsPage() {
<Link
key={weaponId}
to={weaponBuildPage(weaponIdToSlug(weaponId))}
className="builds__category__weapon"
className={styles.categoryWeapon}
data-testid={`weapon-${weaponId}-link`}
>
<Image
className="builds__category__weapon__img"
className={styles.categoryWeaponImg}
path={mainWeaponImageUrl(weaponId)}
width={28}
height={28}

View File

@ -464,11 +464,11 @@ function PostDeleteButton({ id, type }: { id: number; type: Post["type"] }) {
fetcher={fetcher}
>
<SendouButton
className="build__small-text"
className="small-text"
variant="minimal-destructive"
size="small"
type="submit"
icon={<TrashIcon className="build__icon" />}
icon={<TrashIcon className="small-icon" />}
>
{t("common:actions.delete")}
</SendouButton>

View File

@ -795,7 +795,7 @@ function BottomSection({
size="small"
type="submit"
isDisabled={ownTeamReported && !data.match.mapList[0].winnerGroupId}
className="build__small-text mt-4"
className="small-text mt-4"
>
{t("q:match.cancelMatch")}
</SendouButton>

View File

@ -641,12 +641,12 @@ function TrustedUsers() {
submitButtonText="Remove"
>
<SendouButton
className="build__small-text"
className="small-text"
variant="minimal-destructive"
size="small"
type="submit"
>
<TrashIcon className="build__icon" />
<TrashIcon className="small-icon" />
</SendouButton>
</FormWithConfirm>
</div>

View File

@ -551,7 +551,7 @@ function DeletePrivateNoteForm({
]}
>
<SubmitButton variant="minimal-destructive" size="small" type="submit">
<TrashIcon className="build__icon" />
<TrashIcon className="small-icon" />
</SubmitButton>
</FormWithConfirm>
);

View File

@ -193,7 +193,7 @@ export function SwissBracket({
<SendouButton
variant="minimal-destructive"
type="submit"
className="build__small-text mb-4"
className="small-text mb-4"
size="small"
data-testid="reset-round-button"
>

View File

@ -502,7 +502,7 @@ function RemoveStaffButton({
size="small"
data-testid="remove-staff-button"
>
<TrashIcon className="build__icon" />
<TrashIcon className="small-icon" />
</SendouButton>
</FormWithConfirm>
);

View File

@ -268,7 +268,7 @@ function TournamentRegisterInfoTabs() {
submitButtonText="Leave"
>
<SendouButton
className="build__small-text"
className="small-text"
variant="minimal-destructive"
type="submit"
>
@ -694,7 +694,7 @@ function TeamInfo({
<SendouButton
size="small"
variant="minimal-destructive"
className="build__small-text"
className="small-text"
>
{t("tournament:pre.info.unregister")}
</SendouButton>
@ -710,7 +710,7 @@ function TeamInfo({
fields={[["_action", "UNREGISTER"]]}
>
<SendouButton
className="build__small-text"
className="small-text"
variant="minimal-destructive"
size="small"
>

View File

@ -0,0 +1,6 @@
.buildsContainer {
display: grid;
justify-content: center;
gap: var(--s-4) var(--s-3);
grid-template-columns: repeat(auto-fit, 240px);
}

View File

@ -31,6 +31,8 @@ import {
} from "../loaders/u.$identifier.builds.server";
export { loader, action };
import styles from "./u.$identifier.builds.module.css";
export const handle: SendouRouteHandle = {
i18n: ["weapons", "builds", "gear"],
};
@ -100,7 +102,7 @@ export default function UserBuildsPage() {
setWeaponFilter={setWeaponFilter}
/>
{builds.length > 0 ? (
<div className="builds-container">
<div className={styles.buildsContainer}>
{builds.map((build) => (
<BuildCard
key={build.id}

View File

@ -1,42 +0,0 @@
.builds__top-container {
display: flex;
align-items: center;
justify-content: flex-end;
}
.builds__category {
display: flex;
flex-direction: column;
padding: var(--s-3);
border-radius: var(--rounded);
background-color: var(--bg-lighter);
font-size: var(--fonts-sm);
font-weight: 600;
gap: var(--s-4);
}
.builds__category__header {
display: flex;
align-items: center;
gap: var(--s-2-5);
}
.builds__category__weapons {
display: flex;
flex-wrap: wrap;
gap: var(--s-3);
}
.builds__category__weapon {
display: flex;
align-items: center;
color: var(--text);
font-size: var(--fonts-xs);
font-weight: var(--body);
gap: var(--s-1);
}
.builds__category__weapon__img {
border-radius: var(--rounded);
background-color: var(--bg-darker);
}

View File

@ -757,264 +757,15 @@ abbr[title] {
margin-block-start: var(--label-margin);
}
.builds-container {
display: grid;
justify-content: center;
gap: var(--s-4) var(--s-3);
grid-template-columns: repeat(auto-fit, 240px);
}
.builds-buttons {
display: flex;
justify-content: space-between;
align-items: flex-start;
}
.builds-buttons__link {
display: flex;
flex-direction: column;
gap: var(--s-2);
}
@media screen and (min-width: 480px) {
.builds-buttons__link {
flex-direction: row;
}
}
.build__filter {
display: flex;
flex-direction: column;
padding: var(--s-2-5);
border-radius: var(--rounded);
background-color: var(--bg-lighter);
gap: var(--s-2);
}
.build__filter__mode {
gap: var(--s-6);
}
.build__filter__date {
display: flex;
align-items: center;
}
@media screen and (min-width: 750px) {
.build__filter {
flex-direction: row;
}
}
.build__filter__ability {
display: flex;
width: 32px;
align-items: center;
}
.build__filter__ap-select {
width: 75px;
}
.build__filter__date-select {
width: 275px;
}
.build {
position: relative;
display: flex;
flex-direction: column;
padding: var(--s-3);
border-radius: var(--rounded);
background-color: var(--bg-lighter);
gap: var(--s-3);
}
.build__private {
background-color: var(--bg-lighter-transparent);
}
.build__private-text {
display: flex;
justify-content: center;
font-weight: var(--semi-bold);
gap: var(--s-1);
}
.build__private-icon {
width: 16px;
margin-block-end: var(--s-1);
stroke-width: 2px;
}
.build__title {
overflow: hidden;
height: 2.5rem;
font-size: var(--fonts-sm);
line-height: 1.25;
word-wrap: break-all;
}
.build__top-row {
display: flex;
justify-content: space-between;
}
.build__date-author-row {
display: flex;
font-size: var(--fonts-xxs);
gap: var(--s-1);
}
.build__date-author-row__owner {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.build__modes {
position: absolute;
right: 0;
display: flex;
min-width: max-content;
margin-top: -21px;
gap: var(--s-1);
}
.build__weapon {
position: relative;
padding: var(--s-0-5);
border-radius: 50%;
background-color: var(--bg-darker-very-transparent);
}
.build__top500 {
position: absolute;
top: -14px;
right: 8px;
}
.build__weapon-text {
padding-left: var(--s-1);
color: var(--text-lighter);
font-size: var(--fonts-xxs);
font-weight: var(--semi-bold);
}
.build__weapons {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
gap: var(--s-1);
}
.build__gear-abilities {
display: grid;
gap: var(--s-2) var(--s-1);
grid-template-columns: repeat(5, max-content);
place-items: center;
}
.build__gear-abilities__no-gear {
grid-template-columns: repeat(4, max-content);
margin: 0 auto;
margin-top: 10px;
margin-bottom: 1rem;
row-gap: 28px;
}
.build__gear {
border-radius: 50%;
background-color: var(--bg-darker-very-transparent);
overflow: visible;
}
.build__ability {
width: var(--ability-size);
height: var(--ability-size);
padding: 0;
border: 2px solid var(--theme-transparent);
border-radius: 50%;
border-right: 0;
border-bottom: 0;
background: var(--bg-ability);
background-size: 100%;
box-shadow: 0 0 0 1px var(--bg-ability);
transform: scale(1);
transition: all 0.1s ease;
user-select: none;
}
.build__ability.is-drag-target {
background: var(--abilities-button-bg);
transform: scale(1.15);
}
.build__ability.drag-started:not(.drop-allowed) {
filter: grayscale(1);
opacity: 0.3;
pointer-events: none;
}
.build__ability.readonly,
.build__ability.readonly:active {
cursor: default;
transform: none;
}
.build__bottom-row {
display: flex;
height: 100%;
align-items: flex-end;
justify-content: center;
gap: var(--s-4);
}
.build__icon {
.small-icon {
width: 1.2rem;
height: 1.2rem;
}
.build__small-text {
.small-text {
font-size: var(--fonts-xxs) !important;
}
.ability-selector__container {
display: flex;
width: 100%;
flex-direction: column;
justify-content: center;
gap: var(--s-3);
}
.ability-selector__slots {
display: grid;
margin: 0 auto;
gap: var(--s-2);
grid-template-columns: repeat(4, max-content);
place-items: center;
}
.ability-selector__ability-buttons {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: var(--s-1);
touch-action: manipulation;
}
.ability-selector__ability-button {
padding: var(--s-0-5);
border-color: var(--abilities-button-bg);
border-radius: 50%;
background-color: var(--abilities-button-bg);
}
.ability-selector__ability-button.is-dragging {
box-shadow: 0 0 100px inset rgb(255 255 255 / 25%);
}
.colors__summary {
padding: var(--s-2) var(--s-3);
border: 2px solid var(--border);