From fb31799a46dc012a3bf6de0fbc2d433039443116 Mon Sep 17 00:00:00 2001 From: EemeliJ Date: Wed, 15 Jul 2026 11:57:45 +0300 Subject: [PATCH 01/19] split timeline into Timeline and TimelineEntry components --- .../src/preparedPages/AboutPage/ui/About.tsx | 133 +++--------------- .../preparedPages/AboutPage/ui/Timeline.tsx | 63 +++++++++ .../AboutPage/ui/TimelineEntry.tsx | 34 +++++ 3 files changed, 120 insertions(+), 110 deletions(-) create mode 100644 frontend-next-migration/src/preparedPages/AboutPage/ui/Timeline.tsx create mode 100644 frontend-next-migration/src/preparedPages/AboutPage/ui/TimelineEntry.tsx diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx index effc885c7..646b13f87 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx @@ -1,15 +1,9 @@ 'use client'; import cls from './About.module.scss'; -import chevronDown from '@/shared/assets/icons/chevronDown.svg'; import Image from 'next/image'; import heroTop from '@/shared/assets/images/aboutPage/hero-top.png'; -import img2019 from '@/shared/assets/images/aboutPage/about2019.png'; -import img2020 from '@/shared/assets/images/aboutPage/about2020.png'; -import img2021 from '@/shared/assets/images/aboutPage/about2021.png'; -import img2022 from '@/shared/assets/images/aboutPage/about2022.png'; -import img2023 from '@/shared/assets/images/aboutPage/about2023.png'; -import img2024 from '@/shared/assets/images/aboutPage/about2024.png'; import { useGetMembersCountQuery, useGetDemographicsQuery, getBehindYears } from '@/entities/About'; +import Timeline from './Timeline'; export interface Props { title: string; @@ -51,6 +45,7 @@ const About = (props: Props) => { } = props; const { data: projectCount = 0, isLoading: membersLoading } = useGetMembersCountQuery(); + const { data: demographics = { localities: 0, nationalities: 0 }, isLoading: demographicsLoading, @@ -74,142 +69,60 @@ const About = (props: Props) => { sizes="100vw" /> +
-

{title}

+

{title}

+

{description}

-

{keywords}

+ +

{keywords}

+

{isLoading ? '...' : projectCount}

{project}

+

{isLoading ? '...' : demographics.localities}

{locality}

+

{isLoading ? '...' : demographics.nationalities}

{nationality}

+

{behindCount}

{behind}

+

{storytitle}

+
-

2019

-
-
- 2019 year image -
-

{V2019}

-
- -

2020

-
-
- 2020 year image -
-

{V2020}

-
- -

2021

-
-
- 2021 year image -
-

{V2021}

-
- -

2022

-
-
- 2022 year image -
-

{V2022}

-
- -

2023

-
-
- 2023 year image -
-

{V2023}

-
- -

2024

-
-
- 2024 year image -
-

{V2024}

-
- -

2025

-
-

{V2025}

-
- -

2026

-
-

{V2026}

-
- - {'Chevron'}
diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/Timeline.tsx b/frontend-next-migration/src/preparedPages/AboutPage/ui/Timeline.tsx new file mode 100644 index 000000000..c9bd6236f --- /dev/null +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/Timeline.tsx @@ -0,0 +1,63 @@ +import Image, { StaticImageData } from 'next/image'; +import chevronDown from '@/shared/assets/icons/chevronDown.svg'; +import img2019 from '@/shared/assets/images/aboutPage/about2019.png'; +import img2020 from '@/shared/assets/images/aboutPage/about2020.png'; +import img2021 from '@/shared/assets/images/aboutPage/about2021.png'; +import img2022 from '@/shared/assets/images/aboutPage/about2022.png'; +import img2023 from '@/shared/assets/images/aboutPage/about2023.png'; +import img2024 from '@/shared/assets/images/aboutPage/about2024.png'; +import cls from './About.module.scss'; +import TimelineEntry from './TimelineEntry'; + +interface Props { + V2019: string; + V2020: string; + V2021: string; + V2022: string; + V2023: string; + V2024: string; + V2025: string; + V2026: string; +} + +interface TimelineItem { + year: number; + text: string; + image: StaticImageData | null; +} + +const Timeline = ({ V2019, V2020, V2021, V2022, V2023, V2024, V2025, V2026 }: Props) => { + const timeline: TimelineItem[] = [ + { year: 2019, image: img2019, text: V2019 }, + { year: 2020, image: img2020, text: V2020 }, + { year: 2021, image: img2021, text: V2021 }, + { year: 2022, image: img2022, text: V2022 }, + { year: 2023, image: img2023, text: V2023 }, + { year: 2024, image: img2024, text: V2024 }, + { year: 2025, image: null, text: V2025 }, + { year: 2026, image: null, text: V2026 }, + ]; + + return ( + <> + {timeline.map((item) => ( + + ))} + + Chevron + + ); +}; + +export default Timeline; diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/TimelineEntry.tsx b/frontend-next-migration/src/preparedPages/AboutPage/ui/TimelineEntry.tsx new file mode 100644 index 000000000..9a729c0c4 --- /dev/null +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/TimelineEntry.tsx @@ -0,0 +1,34 @@ +import Image, { StaticImageData } from 'next/image'; +import cls from './About.module.scss'; + +interface TimelineEntryProps { + year: number; + text: string; + image?: StaticImageData | null; +} + +const TimelineEntry = ({ year, text, image }: TimelineEntryProps) => { + return ( + <> +

{year}

+ +
+ {image && ( +
+ {`${year} +
+ )} + +

{text}

+
+ + ); +}; + +export default TimelineEntry; From 7b876bb5dcec97b698b48adeac5c6b432912b207 Mon Sep 17 00:00:00 2001 From: EemeliJ Date: Wed, 15 Jul 2026 14:14:35 +0300 Subject: [PATCH 02/19] Implemented collapsible timeline with sorting --- .../AboutPage/ui/About.module.scss | 88 +++++++++++++++---- .../preparedPages/AboutPage/ui/Timeline.tsx | 22 ++++- .../AboutPage/ui/TimelineEntry.tsx | 54 ++++++++---- 3 files changed, 131 insertions(+), 33 deletions(-) diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss index c8c54b53b..ba1bf7c08 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss @@ -92,22 +92,10 @@ } .storygrid { - display: grid; - grid-template-columns: 25% 73%; - grid-template-rows: 6; + display: flex; + flex-direction: column; + gap: var(--spacing-400); white-space: pre-line; - - @media (max-width: breakpoint(xl)) { - grid-template-columns: 35% 65%; - } - - @media (max-width: breakpoint(md)) { - grid-template-columns: 30% 70%; - } - - @media (max-width: breakpoint(sm)) { - grid-template-columns: 100%; - } } .yearImgWrap { @@ -251,3 +239,73 @@ padding-left: var(--spacing-md); } } + +.sortContainer { + display: flex; + justify-content: flex-end; + align-items: center; + gap: var(--spacing-sm); + margin-bottom: var(--spacing-400); + padding-right: var(--spacing-400); +} + +.sortContainer select { + padding: 0.4rem 0.8rem; + border-radius: var(--corner-radius-web); + border: 2px solid var(--black); + background: var(--background); + cursor: pointer; +} + +.timelineHeader { + display: flex; + justify-content: space-between; + align-items: center; + + cursor: pointer; + user-select: none; + padding: var(--spacing-300) 0; +} + +.timelineBody { + width: 100%; +} + +.chevron { + font-size: 1.5rem; + color: var(--primary-color); +} +.timelineCard { + width: 100%; + margin-bottom: var(--spacing-400); + padding: var(--spacing-400); + + background: var(--background); + + border-radius: var(--corner-radius-web); + outline: var(--thickness-web) solid var(--black); + + box-shadow: 6px 6px #121212; +} + +.chevron { + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + margin-left: 1rem; + width: 24px; + height: 24px; + + transform: rotate(0deg); + transition: transform 0.3s ease; + + img { + width: 100%; + height: 100%; + } +} + +.chevronOpen { + transform: rotate(180deg); +} diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/Timeline.tsx b/frontend-next-migration/src/preparedPages/AboutPage/ui/Timeline.tsx index c9bd6236f..3de322886 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/Timeline.tsx +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/Timeline.tsx @@ -1,3 +1,4 @@ +import { useState } from 'react'; import Image, { StaticImageData } from 'next/image'; import chevronDown from '@/shared/assets/icons/chevronDown.svg'; import img2019 from '@/shared/assets/images/aboutPage/about2019.png'; @@ -38,9 +39,28 @@ const Timeline = ({ V2019, V2020, V2021, V2022, V2023, V2024, V2025, V2026 }: Pr { year: 2026, image: null, text: V2026 }, ]; + const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc'); + + const sortedTimeline = [...timeline].sort((a, b) => + sortOrder === 'desc' ? b.year - a.year : a.year - b.year, + ); + return ( <> - {timeline.map((item) => ( +
+ + + +
+ + {sortedTimeline.map((item) => ( { - return ( - <> -

{year}

+ const [isOpen, setIsOpen] = useState(false); -
- {image && ( -
- {`${year} -
- )} + return ( +
+
setIsOpen(!isOpen)} + > +

{year}

-

{text}

+
- + + {isOpen && ( +
+ {image && ( +
+ {`${year} +
+ )} + +

{text}

+
+ )} +
); }; From af283605d460fb063b4c66881cc96ed542d6c5d1 Mon Sep 17 00:00:00 2001 From: EemeliJ Date: Sun, 2 Aug 2026 14:28:16 +0300 Subject: [PATCH 03/19] Refactor: separate timeline sorting logic from timeline rendering --- .../AboutPage/ui/About.module.scss | 5 +- .../src/preparedPages/AboutPage/ui/About.tsx | 47 +++++++++++-------- .../preparedPages/AboutPage/ui/Timeline.tsx | 20 ++------ 3 files changed, 34 insertions(+), 38 deletions(-) diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss index ba1bf7c08..38a5b290a 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss @@ -264,7 +264,10 @@ cursor: pointer; user-select: none; - padding: var(--spacing-300) 0; + + min-height: 48px; + padding: 0 var(--spacing-300); + margin-bottom: var(--spacing-300); } .timelineBody { diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx index 646b13f87..5faac3e3a 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx @@ -1,8 +1,8 @@ 'use client'; +import { useState } from 'react'; import cls from './About.module.scss'; -import Image from 'next/image'; -import heroTop from '@/shared/assets/images/aboutPage/hero-top.png'; import { useGetMembersCountQuery, useGetDemographicsQuery, getBehindYears } from '@/entities/About'; +import { DropdownWrapper } from '@/shared/ui/DropdownWrapperV2/ui/DropdownWrapper'; import Timeline from './Timeline'; export interface Props { @@ -54,21 +54,16 @@ const About = (props: Props) => { const behindCount = getBehindYears(); const isLoading = membersLoading || demographicsLoading; + const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc'); + return (
-
- Hero Image -
+ {storytitle} +

{title}

@@ -103,18 +98,30 @@ const About = (props: Props) => {
-

- {storytitle} -

+
+ setSortOrder('desc'), + }, + { + elementText: 'Vanhin ensin', + onClickCallback: () => setSortOrder('asc'), + }, + ]} + /> +
{ +const Timeline = ({ sortOrder, V2019, V2020, V2021, V2022, V2023, V2024, V2025, V2026 }: Props) => { const timeline: TimelineItem[] = [ { year: 2019, image: img2019, text: V2019 }, { year: 2020, image: img2020, text: V2020 }, @@ -39,27 +40,12 @@ const Timeline = ({ V2019, V2020, V2021, V2022, V2023, V2024, V2025, V2026 }: Pr { year: 2026, image: null, text: V2026 }, ]; - const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc'); - const sortedTimeline = [...timeline].sort((a, b) => sortOrder === 'desc' ? b.year - a.year : a.year - b.year, ); return ( <> -
- - - -
- {sortedTimeline.map((item) => ( Date: Mon, 10 Aug 2026 21:07:33 +0300 Subject: [PATCH 04/19] Adjusted timeline dropdown positioning --- .../AboutPage/ui/About.module.scss | 40 ++++- .../src/preparedPages/AboutPage/ui/About.tsx | 140 +++++++++--------- .../preparedPages/AboutPage/ui/Timeline.tsx | 13 +- .../ui/DropdownWrapper.module.scss | 1 + .../DropdownWrapperV2/ui/DropdownWrapper.tsx | 2 +- 5 files changed, 113 insertions(+), 83 deletions(-) diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss index 38a5b290a..51dd08ce3 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss @@ -96,6 +96,7 @@ flex-direction: column; gap: var(--spacing-400); white-space: pre-line; + width: 100%; } .yearImgWrap { @@ -186,13 +187,38 @@ } #line { + width: 95%; border-left: 10px solid orange; margin-left: 5%; margin-bottom: var(--spacing-600); + box-sizing: border-box; } #History { + width: 30%; padding-bottom: var(--spacing-600); + text-align: left; + margin-bottom: var(--spacing-400); + + @media (min-width: breakpoint(xxl)) and (max-width: 2004px) { + width: 40%; + } + + @media (max-width: breakpoint(xxl)) { + width: 50%; + } + + @media (max-width: breakpoint(xl)) { + width: 50%; + } + + @media (max-width: breakpoint(md)) { + width: 70%; + } + + @media (max-width: breakpoint(sm)) { + width: 90%; + } } .chevronImage { @@ -267,7 +293,7 @@ min-height: 48px; padding: 0 var(--spacing-300); - margin-bottom: var(--spacing-300); + margin-bottom: 0; } .timelineBody { @@ -279,8 +305,12 @@ color: var(--primary-color); } .timelineCard { - width: 100%; + width: calc(100% - var(--spacing-400)); + box-sizing: border-box; + + margin-left: var(--spacing-400); margin-bottom: var(--spacing-400); + padding: var(--spacing-400); background: var(--background); @@ -312,3 +342,9 @@ .chevronOpen { transform: rotate(180deg); } + +.aboutContent { + width: 100%; + display: grid; + justify-items: center; +} diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx index 5faac3e3a..beba7e08a 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx @@ -58,81 +58,85 @@ const About = (props: Props) => { return (
-

- {storytitle} -

- -
-

{title}

- -

{description}

- -

{keywords}

- -
-
-

{isLoading ? '...' : projectCount}

-

{project}

-
- -
-

{isLoading ? '...' : demographics.localities}

-

{locality}

+
+

+ {storytitle} +

+ +
+

{title}

+ +

{description}

+ +

{keywords}

+ +
+
+

{isLoading ? '...' : projectCount}

+

{project}

+
+ +
+

+ {isLoading ? '...' : demographics.localities} +

+

{locality}

+
+ +
+

+ {isLoading ? '...' : demographics.nationalities} +

+

{nationality}

+
+ +
+

{behindCount}

+

{behind}

+
+
-
-

- {isLoading ? '...' : demographics.nationalities} -

-

{nationality}

+
+
+ setSortOrder('desc'), + }, + { + elementText: 'Vanhin ensin', + onClickCallback: () => setSortOrder('asc'), + }, + ]} + />
-
-

{behindCount}

-

{behind}

+
+
- -
-
- setSortOrder('desc'), - }, - { - elementText: 'Vanhin ensin', - onClickCallback: () => setSortOrder('asc'), - }, - ]} - /> -
- -
- -
-
); }; diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/Timeline.tsx b/frontend-next-migration/src/preparedPages/AboutPage/ui/Timeline.tsx index 5191f7643..489b4d3cd 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/Timeline.tsx +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/Timeline.tsx @@ -1,17 +1,14 @@ -import Image, { StaticImageData } from 'next/image'; -import chevronDown from '@/shared/assets/icons/chevronDown.svg'; +import { StaticImageData } from 'next/image'; import img2019 from '@/shared/assets/images/aboutPage/about2019.png'; import img2020 from '@/shared/assets/images/aboutPage/about2020.png'; import img2021 from '@/shared/assets/images/aboutPage/about2021.png'; import img2022 from '@/shared/assets/images/aboutPage/about2022.png'; import img2023 from '@/shared/assets/images/aboutPage/about2023.png'; import img2024 from '@/shared/assets/images/aboutPage/about2024.png'; -import cls from './About.module.scss'; import TimelineEntry from './TimelineEntry'; interface Props { sortOrder: 'asc' | 'desc'; - V2019: string; V2020: string; V2021: string; @@ -54,14 +51,6 @@ const Timeline = ({ sortOrder, V2019, V2020, V2021, V2022, V2023, V2024, V2025, image={item.image} /> ))} - - Chevron ); }; diff --git a/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.module.scss b/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.module.scss index 9a067038e..f552254ae 100644 --- a/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.module.scss +++ b/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.module.scss @@ -91,6 +91,7 @@ display: block; width: 100%; cursor: pointer; + position: relative; } .disabled { diff --git a/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.tsx b/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.tsx index 889a58517..0764c5191 100644 --- a/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.tsx +++ b/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.tsx @@ -197,7 +197,7 @@ export const DropdownWrapper = (props: DropdownWrapperProps) => { transform: isOpen ? 'rotate(180deg)' : 'rotate(0deg)', transition: 'transform 0.4s ease-in-out', position: 'absolute', - right: '10%', + right: '0', marginTop: '7px', }} > From 4d59ff0090021dd1e5b606fdf313cbc3312bc356 Mon Sep 17 00:00:00 2001 From: EemeliJ Date: Tue, 11 Aug 2026 18:24:36 +0300 Subject: [PATCH 05/19] Updated About page layout and statistics, refined title typography, added statistic card styling, simplified the statistics section, aligned the layout with the Figma design, and updated the statistics heading. --- .../src/preparedPages/AboutPage/ui/About.module.scss | 7 +++++++ .../src/preparedPages/AboutPage/ui/About.tsx | 12 ++++-------- .../src/shared/i18n/locales/en/about.json | 2 +- .../src/shared/i18n/locales/fi/about.json | 4 ++-- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss index 51dd08ce3..fcb628400 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss @@ -196,6 +196,7 @@ #History { width: 30%; + font: var(--font-sw-4xl); padding-bottom: var(--spacing-600); text-align: left; margin-bottom: var(--spacing-400); @@ -348,3 +349,9 @@ display: grid; justify-items: center; } + +.statItem { + background: var(--background); + border: 2px solid var(--black); + border-radius: var(--corner-radius-web); +} diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx index beba7e08a..87ae6f57a 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx @@ -69,31 +69,27 @@ const About = (props: Props) => {

{title}

-

{description}

- -

{keywords}

-
-
+

{isLoading ? '...' : projectCount}

{project}

-
+

{isLoading ? '...' : demographics.localities}

{locality}

-
+

{isLoading ? '...' : demographics.nationalities}

{nationality}

-
+

{behindCount}

{behind}

diff --git a/frontend-next-migration/src/shared/i18n/locales/en/about.json b/frontend-next-migration/src/shared/i18n/locales/en/about.json index c05b5d363..bd29aa4c5 100644 --- a/frontend-next-migration/src/shared/i18n/locales/en/about.json +++ b/frontend-next-migration/src/shared/i18n/locales/en/about.json @@ -1,6 +1,6 @@ { "title": "About Us", - "head-title": "What is PRG?", + "head-title": "A lot of talented people work on this project", "head-description": "Psyche's Royale Gaming ry is a non-profit game art association that is collaboratively developing the mobile game ALT Zone 1.0", "head-keywords": "ALT Zone, ALT Zone mobile game, community game development, participate in game creation, game art mobile game", "og-title": "Psyche's Royale Gaming ry – Creators and Community of ALT Zone Game Art", diff --git a/frontend-next-migration/src/shared/i18n/locales/fi/about.json b/frontend-next-migration/src/shared/i18n/locales/fi/about.json index 83d3a6165..72695aee0 100644 --- a/frontend-next-migration/src/shared/i18n/locales/fi/about.json +++ b/frontend-next-migration/src/shared/i18n/locales/fi/about.json @@ -1,11 +1,11 @@ { "title": "Meistä", - "head-title": "Mikä PRG?", + "head-title": "Tämän projektin parissa työskentelee paljon erilaisia osaajia", "head-description": "Psyche's Royale Gaming ry on voittoa tavoittelematon pelitaiteen yhdistys, joka valmistaa ALT Zone 1.0 mobiilipeliä yhteisöllisesti.", "head-keywords": "ALT Zone, ALT Zone mobiilipeli, yhteisöllinen pelinkehitys, osallistu pelin tekemiseen, pelitaide mobiilipeli", "og-title": "Psyche's Royale Gaming ry – ALT Zone -pelitaiteen tekijät ja yhteisö", "og-description": "PRG ry on suomalainen voittoa tavoittelematon yhdistys, joka kehittää ALT Zone -mobiilipeliä yhdessä nuorten, vapaaehtoisten ja pelialan osaajien kanssa.", - "project": "On työskennellyt tässä projectissa", + "project": "On työskennellyt tässä projektissa", "locality": "Eri paikkakunnalta", "nationality": "Eri kansalaisuutta", "behind": "Vuotta matkaa takana", From 8272d83aff150d011724b634e1acc5c1aa9d2620 Mon Sep 17 00:00:00 2001 From: EemeliJ Date: Wed, 12 Aug 2026 18:58:10 +0300 Subject: [PATCH 06/19] Refined the statistics card typography, spacing and layout --- .../AboutPage/ui/About.module.scss | 43 ++++++++++++++++--- .../src/preparedPages/AboutPage/ui/About.tsx | 4 +- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss index fcb628400..162a19d24 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss @@ -26,7 +26,7 @@ padding: 1.2rem; width: 30%; - @media (min-width: 1400px) and (max-width: 2004px) { + @media (min-width: breakpoint(xxl)) and (max-width: 2004px) { width: 40%; } @@ -79,15 +79,19 @@ .headergrid { display: grid; - grid-template-columns: 25% 25% 25% 25%; + grid-template-columns: repeat(4, 1fr); + gap: 32px; + padding: 0 64px; grid-template-rows: 1; @media (max-width: breakpoint(md)) { grid-template-columns: 50% 50%; + padding: 0 24px; } @media (max-width: breakpoint(sm)) { grid-template-columns: 50% 50%; + padding: 0 16px; } } @@ -154,9 +158,14 @@ } .sValues { - font: var(font-sw-xl); + font: var(--font-sw-xxl); + color: #FFA100; + font-weight: 400; + line-height: 100%; + letter-spacing: -0.04em; text-align: center; - padding: 1%; + padding: 0; + margin: 0 0 2px; } .yearh1 { font: var(--font-sw-xl); @@ -351,7 +360,31 @@ } .statItem { - background: var(--background); + background: #2B516A; border: 2px solid var(--black); border-radius: var(--corner-radius-web); + padding: 6px 8px; + min-height: 68px; + box-sizing: border-box; + + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.statItem .gridp { + font: var(--font-dm-bold-s); + font-size: 12px; + line-height: 1.2; + text-align: center; + padding: 0; + margin: 0; +} + +.statsTitle { + font: var(--font-sw-l); + color: var(--primary-color); + text-align: center; + margin: 0; } diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx index 87ae6f57a..61d5e9194 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx @@ -27,8 +27,6 @@ export interface Props { const About = (props: Props) => { const { title, - description, - keywords, storytitle, project, locality, @@ -67,7 +65,7 @@ const About = (props: Props) => {

-

{title}

+

{title}

From 6174e0a0d36bd51d5c27c69199a15a7a81ff5bac Mon Sep 17 00:00:00 2001 From: EemeliJ Date: Thu, 13 Aug 2026 18:16:46 +0300 Subject: [PATCH 07/19] Fixed responsive statistic card layout, wrapping and breakpointbehaviour --- .../AboutPage/ui/About.module.scss | 89 ++++++++++++++----- .../src/preparedPages/AboutPage/ui/About.tsx | 4 +- 2 files changed, 70 insertions(+), 23 deletions(-) diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss index 162a19d24..ee6b7326c 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss @@ -3,19 +3,6 @@ justify-items: center; } -.hero { - position: relative; - width: 100%; - max-width: 1400px; - aspect-ratio: 16 / 9; - margin-bottom: var(--spacing-600); -} - -.heroImg { - object-fit: cover; - max-width: 1400px; -} - .containerTop { background: var(--background); text-align: center; @@ -25,6 +12,7 @@ margin-bottom: 5%; padding: 1.2rem; width: 30%; + box-sizing: border-box; @media (min-width: breakpoint(xxl)) and (max-width: 2004px) { width: 40%; @@ -79,19 +67,48 @@ .headergrid { display: grid; - grid-template-columns: repeat(4, 1fr); + grid-template-columns: repeat(4, 130px); gap: 32px; - padding: 0 64px; + padding: 8px 0 0; + width: 100%; + box-sizing: border-box; + justify-content: center; grid-template-rows: 1; + @media (max-width: breakpoint(xxl)) { + grid-template-columns: repeat(2, 130px); + gap: 20px 28px; + justify-content: center; + } + + @media (max-width: breakpoint(xl)) { + grid-template-columns: repeat(2, 130px); + gap: 16px 24px; + justify-content: center; + } + + @media (max-width: breakpoint(lg)) { + grid-template-columns: repeat(2, 130px); + gap: 16px; + justify-content: center; + } + @media (max-width: breakpoint(md)) { - grid-template-columns: 50% 50%; - padding: 0 24px; + grid-template-columns: repeat(2, 130px); + gap: 12px; + justify-content: center; } @media (max-width: breakpoint(sm)) { - grid-template-columns: 50% 50%; - padding: 0 16px; + grid-template-columns: repeat(2, 130px); + gap: 12px; + justify-content: center; + } + + @media (max-width: 1600px) { + grid-template-columns: repeat(2, 130px); + gap: 16px 24px; + justify-content: center; } } @@ -364,7 +381,8 @@ border: 2px solid var(--black); border-radius: var(--corner-radius-web); padding: 6px 8px; - min-height: 68px; + width: 130px; + height: 84px; box-sizing: border-box; display: flex; @@ -373,6 +391,12 @@ justify-content: center; } +.statItemNarrow .gridp { + max-width: 90px; + margin-left: auto; + margin-right: auto; +} + .statItem .gridp { font: var(--font-dm-bold-s); font-size: 12px; @@ -380,11 +404,34 @@ text-align: center; padding: 0; margin: 0; + white-space: normal; } .statsTitle { font: var(--font-sw-l); + font-size: 22px; color: var(--primary-color); text-align: center; - margin: 0; + margin: 0 auto; + letter-spacing: -0.03em; + width: 100%; + max-width: 100%; + box-sizing: border-box; + white-space: normal; + overflow-wrap: break-word; + + @media (max-width: breakpoint(xl)) { + font-size: 20px; + line-height: 1.2; + } + + @media (max-width: breakpoint(md)) { + font-size: 18px; + line-height: 1.2; + } + + @media (max-width: breakpoint(sm)) { + font-size: 16px; + line-height: 1.2; + } } diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx index 61d5e9194..50ce0f734 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx @@ -73,14 +73,14 @@ const About = (props: Props) => {

{project}

-
+

{isLoading ? '...' : demographics.localities}

{locality}

-
+

{isLoading ? '...' : demographics.nationalities}

From ce6fb343bda61bff1c15050ca12f1cbcbe37a803 Mon Sep 17 00:00:00 2001 From: EemeliJ Date: Fri, 14 Aug 2026 18:48:34 +0300 Subject: [PATCH 08/19] Refined spacing, layout and markers of the timeline element --- .../AboutPage/ui/About.module.scss | 84 +++++++++++++++++-- .../src/shared/i18n/locales/en/about.json | 4 +- 2 files changed, 79 insertions(+), 9 deletions(-) diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss index ee6b7326c..56f483c35 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss @@ -213,11 +213,48 @@ } #line { + position: relative; + width: 95%; - border-left: 10px solid orange; margin-left: 5%; margin-bottom: var(--spacing-600); + box-sizing: border-box; + border-left: none; +} + +#line::before { + content: ''; + position: absolute; + + width: 5px; + + background: #FFA100; + + left: -9px; + top: 38px; + bottom: 40px; + + z-index: 1; +} + +#line::after { + content: ''; + position: absolute; + + width: 16px; + height: 16px; + + border-left: 4px solid #FFA100; + border-top: 4px solid #FFA100; + + left: -14.5px; + top: 28px; + + transform: rotate(45deg); + + box-sizing: border-box; + z-index: 2; } #History { @@ -318,11 +355,15 @@ cursor: pointer; user-select: none; - min-height: 48px; - padding: 0 var(--spacing-300); + min-height: 64px; + padding: 0 16px; margin-bottom: 0; } +.timelineHeader .yearh1::before { + display: none; +} + .timelineBody { width: 100%; } @@ -331,16 +372,21 @@ font-size: 1.5rem; color: var(--primary-color); } + .timelineCard { - width: calc(100% - var(--spacing-400)); + position: relative; + + width: calc(100% - (2 * var(--spacing-400))); box-sizing: border-box; margin-left: var(--spacing-400); - margin-bottom: var(--spacing-400); + margin-bottom: 20px; - padding: var(--spacing-400); + padding: 0 24px; - background: var(--background); + min-height: 64px; + + background: #2B516A; border-radius: var(--corner-radius-web); outline: var(--thickness-web) solid var(--black); @@ -348,6 +394,30 @@ box-shadow: 6px 6px #121212; } +.timelineCard::before { + content: ''; + position: absolute; + + width: 28px; + height: 28px; + + border: 4px solid #FFA100; + border-radius: 50%; + + background: var(--background); + + left: -45px; + top: 50%; + transform: translateY(-50%); + + box-sizing: border-box; + z-index: 2; +} + +.timelineCard:first-child::before { + display: none; +} + .chevron { display: flex; align-items: center; diff --git a/frontend-next-migration/src/shared/i18n/locales/en/about.json b/frontend-next-migration/src/shared/i18n/locales/en/about.json index bd29aa4c5..88dc434d6 100644 --- a/frontend-next-migration/src/shared/i18n/locales/en/about.json +++ b/frontend-next-migration/src/shared/i18n/locales/en/about.json @@ -7,8 +7,8 @@ "og-description": "PRG ry is a Finnish non-profit association developing the ALT Zone mobile game in collaboration with youth, volunteers, and game industry professionals.", "project": "Has worked on this project", - "locality": "From a different locality", - "nationality": "From a different nationality", + "locality": "Localities represented", + "nationality": "Nationalities represented", "behind": "Years into the journey", "story-title": "ALT Zone's history", From 96956a805eabe1580e8d8ba07d85e977feb5f10e Mon Sep 17 00:00:00 2001 From: EemeliJ Date: Sun, 16 Aug 2026 20:03:54 +0300 Subject: [PATCH 09/19] Updated the timeline sorting dropdown layout, styling and selection states, also fixed some positioning and rendering issues with the ellipses and arrows in the timeline --- .../AboutPage/ui/About.module.scss | 153 ++++++++++++++---- .../src/preparedPages/AboutPage/ui/About.tsx | 10 +- .../preparedPages/AboutPage/ui/Timeline.tsx | 5 +- .../AboutPage/ui/TimelineEntry.tsx | 11 +- .../DropdownWrapperV2/ui/DropdownWrapper.tsx | 4 +- 5 files changed, 151 insertions(+), 32 deletions(-) diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss index 56f483c35..85995162c 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss @@ -36,6 +36,8 @@ } .containerBottom { + position: relative; + background: var(--background); text-align: center; border-radius: var(--corner-radius-web); @@ -158,6 +160,7 @@ font: var(--font-sw-l); } } + .p { font: var(--font-dm-s); text-align: left; @@ -184,6 +187,7 @@ padding: 0; margin: 0 0 2px; } + .yearh1 { font: var(--font-sw-xl); color: var(--primary-color); @@ -214,11 +218,9 @@ #line { position: relative; - width: 95%; margin-left: 5%; margin-bottom: var(--spacing-600); - box-sizing: border-box; border-left: none; } @@ -226,37 +228,50 @@ #line::before { content: ''; position: absolute; - width: 5px; - background: #FFA100; - left: -9px; top: 38px; bottom: 40px; - z-index: 1; } #line::after { content: ''; position: absolute; - width: 16px; height: 16px; - border-left: 4px solid #FFA100; border-top: 4px solid #FFA100; - left: -14.5px; top: 28px; - transform: rotate(45deg); - box-sizing: border-box; z-index: 2; } +#line.timelineNewest::before { + top: 4px; + bottom: 40px; +} + +#line.timelineOldest::before { + top: 40px; + bottom: 4px; +} + +#line.timelineNewest::after { + top: 2px; + bottom: auto; + transform: rotate(45deg); +} + +#line.timelineOldest::after { + top: auto; + bottom: -2px; + transform: rotate(225deg); +} + #History { width: 30%; font: var(--font-sw-4xl); @@ -330,21 +345,106 @@ } } +/* + * Timeline sort dropdown + */ .sortContainer { - display: flex; - justify-content: flex-end; - align-items: center; - gap: var(--spacing-sm); - margin-bottom: var(--spacing-400); - padding-right: var(--spacing-400); + position: absolute; + top: 0; + right: 0; + z-index: 20; } -.sortContainer select { - padding: 0.4rem 0.8rem; +.timelineSortDropdown { + position: relative; + width: 180px; + z-index: 20; +} + +.timelineSortHeader { + position: relative; + box-sizing: border-box; + width: 180px; + min-height: 40px; + padding: 8px 14px; + + background: #1E3544; border-radius: var(--corner-radius-web); - border: 2px solid var(--black); - background: var(--background); + outline: var(--thickness-web) solid var(--black); + box-shadow: 6px 6px #121212; +} + +.timelineSortContent { + position: absolute; + top: 100%; + right: 0; + + box-sizing: border-box; + width: 180px; + padding: 8px 10px 10px; + + background: #1E3544; + border-radius: 0 0 var(--corner-radius-web) var(--corner-radius-web); + outline: var(--thickness-web) solid var(--black); + box-shadow: 6px 6px #121212; + + z-index: 21; +} + +.timelineSortItem { + position: relative; + display: flex; + align-items: center; + + min-height: 40px; + padding-left: 34px; + + box-sizing: border-box; + + color: var(--white); cursor: pointer; + text-decoration: none; +} + +.timelineSortItem::before { + content: ''; + + position: absolute; + left: 0; + + width: 20px; + height: 20px; + + box-sizing: border-box; + + background: #d9d9d9; + border: 1px solid #121212; + border-radius: 2px; +} + +.timelineSortItem.active { + color: var(--white) !important; + text-decoration: none !important; +} + +.timelineSortItem.active::before { + background: #FFA100; +} + +.timelineSortItem.active::after { + content: ''; + + position: absolute; + left: 5px; + top: 12px; + + width: 8px; + height: 4px; + + border-left: 3px solid #121212; + border-bottom: 3px solid #121212; + + transform: rotate(-45deg); } .timelineHeader { @@ -368,11 +468,6 @@ width: 100%; } -.chevron { - font-size: 1.5rem; - color: var(--primary-color); -} - .timelineCard { position: relative; @@ -414,7 +509,11 @@ z-index: 2; } -.timelineCard:first-child::before { +.timelineNewest .timelineStart::before { + display: none; +} + +.timelineOldest .timelineEnd::before { display: none; } diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx index 50ce0f734..67b7f3304 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx @@ -100,13 +100,19 @@ const About = (props: Props) => { dynamicTitle="Järjestä" showArrow autoClose + className={cls.timelineSortDropdown} + headerClassName={cls.timelineSortHeader} + contentClassName={cls.timelineSortContent} + contentItemClassName={cls.timelineSortItem} elements={[ { elementText: 'Uusin ensin', + active: sortOrder === 'desc', onClickCallback: () => setSortOrder('desc'), }, { elementText: 'Vanhin ensin', + active: sortOrder === 'asc', onClickCallback: () => setSortOrder('asc'), }, ]} @@ -114,7 +120,9 @@ const About = (props: Props) => {
- {sortedTimeline.map((item) => ( + {sortedTimeline.map((item, index) => ( ))} diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/TimelineEntry.tsx b/frontend-next-migration/src/preparedPages/AboutPage/ui/TimelineEntry.tsx index 870640e70..c1d29cc20 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/TimelineEntry.tsx +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/TimelineEntry.tsx @@ -7,13 +7,20 @@ interface TimelineEntryProps { year: number; text: string; image?: StaticImageData | null; + sortOrder: 'asc' | 'desc'; + isFirst: boolean; + isLast: boolean; } -const TimelineEntry = ({ year, text, image }: TimelineEntryProps) => { +const TimelineEntry = ({ year, text, image, sortOrder, isFirst, isLast }: TimelineEntryProps) => { const [isOpen, setIsOpen] = useState(false); return ( -
+
setIsOpen(!isOpen)} diff --git a/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.tsx b/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.tsx index 0764c5191..8fc2afac3 100644 --- a/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.tsx +++ b/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.tsx @@ -272,7 +272,9 @@ export const DropdownWrapper = (props: DropdownWrapperProps) => { ) : ( Date: Mon, 17 Aug 2026 15:28:32 +0300 Subject: [PATCH 10/19] Made some fixes to the sorting dropdown, such as positioning and checkbox styling --- .../AboutPage/ui/About.module.scss | 68 ++++++++++++++----- .../src/preparedPages/AboutPage/ui/About.tsx | 49 ++++++------- .../ui/DropdownWrapperV2/types/index.ts | 1 + .../DropdownWrapperV2/ui/DropdownWrapper.tsx | 9 ++- 4 files changed, 84 insertions(+), 43 deletions(-) diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss index 85995162c..2acb00aab 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss @@ -46,6 +46,8 @@ margin-bottom: 5%; width: 30%; + transition: margin-top 0.3s ease; + @media (min-width: breakpoint(xxl)) and (max-width: 2004px) { width: 40%; } @@ -345,14 +347,40 @@ } } +/* + * Timeline sort dropdown + */ /* * Timeline sort dropdown */ .sortContainer { - position: absolute; - top: 0; - right: 0; + width: 30%; + display: flex; + justify-content: flex-end; + + margin-bottom: 12px; + z-index: 20; + + @media (min-width: breakpoint(xxl)) and (max-width: 2004px) { + width: 40%; + } + + @media (max-width: breakpoint(xxl)) { + width: 50%; + } + + @media (max-width: breakpoint(xl)) { + width: 50%; + } + + @media (max-width: breakpoint(md)) { + width: 70%; + } + + @media (max-width: breakpoint(sm)) { + width: 90%; + } } .timelineSortDropdown { @@ -364,26 +392,28 @@ .timelineSortHeader { position: relative; box-sizing: border-box; + width: 180px; min-height: 40px; padding: 8px 14px; background: #1E3544; + border-radius: var(--corner-radius-web); outline: var(--thickness-web) solid var(--black); box-shadow: 6px 6px #121212; } .timelineSortContent { - position: absolute; - top: 100%; - right: 0; + position: relative; box-sizing: border-box; width: 180px; + padding: 8px 10px 10px; background: #1E3544; + border-radius: 0 0 var(--corner-radius-web) var(--corner-radius-web); outline: var(--thickness-web) solid var(--black); box-shadow: 6px 6px #121212; @@ -393,6 +423,7 @@ .timelineSortItem { position: relative; + display: flex; align-items: center; @@ -401,9 +432,9 @@ box-sizing: border-box; - color: var(--white); + color: var(--white) !important; cursor: pointer; - text-decoration: none; + text-decoration: none !important; } .timelineSortItem::before { @@ -411,6 +442,7 @@ position: absolute; left: 0; + top: 50%; width: 20px; height: 20px; @@ -420,31 +452,33 @@ background: #d9d9d9; border: 1px solid #121212; border-radius: 2px; + + transform: translateY(-50%); } -.timelineSortItem.active { +.timelineSortItemActive { color: var(--white) !important; text-decoration: none !important; } -.timelineSortItem.active::before { +.timelineSortItemActive::before { background: #FFA100; } -.timelineSortItem.active::after { +.timelineSortItemActive::after { content: ''; position: absolute; - left: 5px; - top: 12px; + left: 6px; + top: 50%; - width: 8px; - height: 4px; + width: 6px; + height: 11px; - border-left: 3px solid #121212; + border-right: 3px solid #121212; border-bottom: 3px solid #121212; - transform: rotate(-45deg); + transform: translateY(-65%) rotate(45deg); } .timelineHeader { diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx index 67b7f3304..fc6cced8f 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx @@ -94,31 +94,32 @@ const About = (props: Props) => {
-
-
- setSortOrder('desc'), - }, - { - elementText: 'Vanhin ensin', - active: sortOrder === 'asc', - onClickCallback: () => setSortOrder('asc'), - }, - ]} - /> -
+
+ setSortOrder('desc'), + }, + { + elementText: 'Vanhin ensin', + active: sortOrder === 'asc', + onClickCallback: () => setSortOrder('asc'), + }, + ]} + /> +
+
; onOpen?: () => void; diff --git a/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.tsx b/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.tsx index 8fc2afac3..786b0eb1c 100644 --- a/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.tsx +++ b/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.tsx @@ -17,6 +17,7 @@ export const DropdownWrapper = (props: DropdownWrapperProps) => { className = '', contentClassName = '', contentItemClassName = '', + activeItemClassName = '', elements, isDisabled, children, @@ -153,6 +154,7 @@ export const DropdownWrapper = (props: DropdownWrapperProps) => { }; const mainElementClass = isDisabled?.status ? cls.disabled : ''; + return (
{ > {'Chevron'} @@ -220,6 +222,7 @@ export const DropdownWrapper = (props: DropdownWrapperProps) => {
)} + {shouldRender && (
{ isExternal={element.link.isExternal} className={classNames(contentItemClassName, { [cls.active]: element.active, + [activeItemClassName]: element.active, })} onClick={() => { if (!openByDefault && !staticDropdown) { @@ -259,7 +263,7 @@ export const DropdownWrapper = (props: DropdownWrapperProps) => { {element.link.isExternal && ( { Date: Tue, 18 Aug 2026 18:05:39 +0300 Subject: [PATCH 11/19] Fixed some positioning in the timeline, and switched pseudo arrow to the chevron svg --- .../AboutPage/ui/About.module.scss | 65 +++++++++---------- .../src/preparedPages/AboutPage/ui/About.tsx | 9 +++ 2 files changed, 39 insertions(+), 35 deletions(-) diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss index 2acb00aab..ff0cdd8f7 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss @@ -119,9 +119,13 @@ .storygrid { display: flex; flex-direction: column; - gap: var(--spacing-400); + gap: 36px; white-space: pre-line; width: 100%; + box-sizing: border-box; + + margin-top: 28px; + margin-bottom: 28px; } .yearImgWrap { @@ -194,7 +198,7 @@ font: var(--font-sw-xl); color: var(--primary-color); text-align: left; - margin-top: -16px; + margin-top: 0; @media (max-width: breakpoint(sm)) { text-align: left; @@ -210,7 +214,7 @@ @media (max-width: breakpoint(lg)) { font: var(--font-sw-l); - margin-top: -12px; + margin-top: 0; } } @@ -222,11 +226,21 @@ position: relative; width: 95%; margin-left: 5%; - margin-bottom: var(--spacing-600); box-sizing: border-box; border-left: none; } +.timelineChevron { + position: absolute; + + width: 24px; + height: 24px; + + left: -18.5px; + + z-index: 2; +} + #line::before { content: ''; position: absolute; @@ -236,42 +250,26 @@ top: 38px; bottom: 40px; z-index: 1; -} - -#line::after { - content: ''; - position: absolute; - width: 16px; - height: 16px; - border-left: 4px solid #FFA100; - border-top: 4px solid #FFA100; - left: -14.5px; - top: 28px; - transform: rotate(45deg); - box-sizing: border-box; - z-index: 2; + border-radius: 999px; } #line.timelineNewest::before { - top: 4px; + top: 40px; bottom: 40px; } #line.timelineOldest::before { top: 40px; - bottom: 4px; + bottom: 40px; } -#line.timelineNewest::after { - top: 2px; - bottom: auto; - transform: rotate(45deg); +.timelineNewest .timelineChevron { + top: 22px; + transform: rotate(180deg); } -#line.timelineOldest::after { - top: auto; - bottom: -2px; - transform: rotate(225deg); +.timelineOldest .timelineChevron { + bottom: 22px; } #History { @@ -347,12 +345,6 @@ } } -/* - * Timeline sort dropdown - */ -/* - * Timeline sort dropdown - */ .sortContainer { width: 30%; display: flex; @@ -509,7 +501,6 @@ box-sizing: border-box; margin-left: var(--spacing-400); - margin-bottom: 20px; padding: 0 24px; @@ -523,6 +514,10 @@ box-shadow: 6px 6px #121212; } +.timelineCard:last-child { + margin-bottom: 0; +} + .timelineCard::before { content: ''; position: absolute; diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx index fc6cced8f..f6962305f 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx @@ -1,9 +1,11 @@ 'use client'; +import Image from 'next/image'; import { useState } from 'react'; import cls from './About.module.scss'; import { useGetMembersCountQuery, useGetDemographicsQuery, getBehindYears } from '@/entities/About'; import { DropdownWrapper } from '@/shared/ui/DropdownWrapperV2/ui/DropdownWrapper'; import Timeline from './Timeline'; +import chevronDown from '@/shared/assets/icons/chevronDown.svg'; export interface Props { title: string; @@ -126,6 +128,13 @@ const About = (props: Props) => { }`} id={cls.line} > + + Date: Wed, 19 Aug 2026 18:13:06 +0300 Subject: [PATCH 12/19] Refined the sorting dropdown UI --- .../AboutPage/ui/About.module.scss | 55 +++++++++++++++---- .../src/preparedPages/AboutPage/ui/About.tsx | 1 + .../ui/DropdownWrapperV2/types/index.ts | 1 + .../ui/DropdownWrapper.module.scss | 23 ++++++++ .../DropdownWrapperV2/ui/DropdownWrapper.tsx | 25 +++++---- 5 files changed, 84 insertions(+), 21 deletions(-) diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss index ff0cdd8f7..a92c56ad1 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss @@ -377,38 +377,66 @@ .timelineSortDropdown { position: relative; - width: 180px; + width: 220px; + max-height: 40px; + + overflow: hidden; + box-sizing: border-box; + + background: #1E3544; + + border-radius: var(--corner-radius-web); + outline: var(--thickness-web) solid var(--black); + box-shadow: 6px 6px #121212; + z-index: 20; + + transition: max-height 0.3s ease; +} + +.timelineSortDropdown[data-dropdown-state='opening'], +.timelineSortDropdown[data-dropdown-state='open'] { + max-height: 140px; +} + +.timelineSortDropdown[data-dropdown-state='closing'] { + max-height: 40px; } .timelineSortHeader { position: relative; box-sizing: border-box; - width: 180px; + width: 100%; min-height: 40px; padding: 8px 14px; - background: #1E3544; + background: transparent; - border-radius: var(--corner-radius-web); - outline: var(--thickness-web) solid var(--black); - box-shadow: 6px 6px #121212; + border-radius: 0; + + outline: none; + box-shadow: none; +} + +.timelineSortChevron { + width: 16px; + height: 16px; } .timelineSortContent { position: relative; box-sizing: border-box; - width: 180px; + width: 100%; padding: 8px 10px 10px; - background: #1E3544; + background: transparent; - border-radius: 0 0 var(--corner-radius-web) var(--corner-radius-web); - outline: var(--thickness-web) solid var(--black); - box-shadow: 6px 6px #121212; + border: none; + outline: none; + box-shadow: none; z-index: 21; } @@ -424,6 +452,11 @@ box-sizing: border-box; + font: var(--font-dm-bold-m); + font-size: 16px; + line-height: 1.2 !important; + white-space: nowrap; + color: var(--white) !important; cursor: pointer; text-decoration: none !important; diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx index f6962305f..f3a31b033 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx @@ -106,6 +106,7 @@ const About = (props: Props) => { contentClassName={cls.timelineSortContent} contentItemClassName={cls.timelineSortItem} activeItemClassName={cls.timelineSortItemActive} + chevronClassName={cls.timelineSortChevron} elements={[ { elementText: 'Uusin ensin', diff --git a/frontend-next-migration/src/shared/ui/DropdownWrapperV2/types/index.ts b/frontend-next-migration/src/shared/ui/DropdownWrapperV2/types/index.ts index 17f36cc96..cdcefe1f5 100644 --- a/frontend-next-migration/src/shared/ui/DropdownWrapperV2/types/index.ts +++ b/frontend-next-migration/src/shared/ui/DropdownWrapperV2/types/index.ts @@ -46,4 +46,5 @@ export type DropdownWrapperProps = { autoClose?: boolean; headerClassName?: string; childrenWrapperClassName?: string; + chevronClassName?: string; }; diff --git a/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.module.scss b/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.module.scss index f552254ae..f04edbd0a 100644 --- a/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.module.scss +++ b/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.module.scss @@ -11,6 +11,29 @@ } } +.chevronWrapper { + position: absolute; + right: 0; + top: 50%; + + display: flex; + align-items: center; + justify-content: center; + + width: 24px; + height: 24px; + + flex-shrink: 0; + + transform: translateY(-50%) rotate(0deg); + transform-origin: center; + transition: transform 0.3s ease; +} + +.chevronWrapperOpen { + transform: translateY(-50%) rotate(180deg); +} + .dropdownContent { padding-top: 10px; line-height: 40px !important; diff --git a/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.tsx b/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.tsx index 786b0eb1c..d108a0988 100644 --- a/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.tsx +++ b/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.tsx @@ -31,6 +31,7 @@ export const DropdownWrapper = (props: DropdownWrapperProps) => { autoClose = false, headerClassName = '', childrenWrapperClassName = '', + chevronClassName = '', } = props; const [isOpen, setIsOpen] = useState(openByDefault || staticDropdown); @@ -78,6 +79,7 @@ export const DropdownWrapper = (props: DropdownWrapperProps) => { setIsOpen(false); } }; + document.addEventListener('az:dropdown-select' as any, handleDropdownSelect as any); return () => { document.removeEventListener('az:dropdown-select' as any, handleDropdownSelect as any); @@ -109,6 +111,7 @@ export const DropdownWrapper = (props: DropdownWrapperProps) => { clearTimeout(closeTimer); setCloseTimer(null); } + setIsOpen(true); }; @@ -119,6 +122,7 @@ export const DropdownWrapper = (props: DropdownWrapperProps) => { setIsOpen(false); setCloseTimer(null); }, 200); + setCloseTimer(timer); }; @@ -159,9 +163,17 @@ export const DropdownWrapper = (props: DropdownWrapperProps) => {
{ {dynamicTitle} {showArrow && ( Chevron )} @@ -294,7 +300,6 @@ export const DropdownWrapper = (props: DropdownWrapperProps) => { element.onClickCallback?.(); }} > - {/**/} {element.elementText} )} From 26199fcb882b2582861ff4f48744f079c42ec66a Mon Sep 17 00:00:00 2001 From: EemeliJ Date: Thu, 20 Aug 2026 19:07:49 +0300 Subject: [PATCH 13/19] Added animations to the timeline entries, and button for closing --- .../AboutPage/ui/About.module.scss | 63 ++++++++++++++++++- .../src/preparedPages/AboutPage/ui/About.tsx | 4 +- .../AboutPage/ui/TimelineEntry.tsx | 18 +++++- 3 files changed, 77 insertions(+), 8 deletions(-) diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss index a92c56ad1..00f2be40f 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss @@ -134,9 +134,7 @@ aspect-ratio: 16 / 9; border-radius: var(--corner-radius-web); overflow: hidden; - outline: 4px solid var(--black); - box-shadow: 6px 6px #121212; - margin: -16px 0 var(--spacing-400) 0; + margin: 0 0 var(--spacing-400) 0; @media (max-width: breakpoint(sm)) { max-width: none; @@ -524,7 +522,66 @@ } .timelineBody { + display: grid; + grid-template-rows: 0fr; + width: 100%; + + opacity: 0; + + transition: + grid-template-rows 0.3s ease, + opacity 0.2s ease; +} + +.timelineBodyOpen { + grid-template-rows: 1fr; + opacity: 1; +} + +.timelineBodyInner { + min-height: 0; + overflow: hidden; +} + +.timelineCloseArea { + width: 100%; + padding-top: var(--spacing-400); + padding-bottom: var(--spacing-400); + + display: flex; + flex-direction: column; + align-items: center; +} + +.timelineDivider { width: 100%; + height: 4px; + background: #FFA100; + margin-bottom: var(--spacing-400); +} + +.timelineCloseButton { + width: 250px; + min-height: 40px; + + padding: 6px 24px; + + background: #FFA100; + color: #121212; + + border: var(--thickness-web) solid #121212; + border-radius: 999px; + + box-shadow: 4px 4px #121212; + + font: var(--font-dm-bold-s); + cursor: pointer; + + display: flex; + align-items: center; + justify-content: center; + + box-sizing: border-box; } .timelineCard { diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx index f3a31b033..cc18c50bc 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx @@ -109,12 +109,12 @@ const About = (props: Props) => { chevronClassName={cls.timelineSortChevron} elements={[ { - elementText: 'Uusin ensin', + elementText: 'Uusin', active: sortOrder === 'desc', onClickCallback: () => setSortOrder('desc'), }, { - elementText: 'Vanhin ensin', + elementText: 'Vanhin', active: sortOrder === 'asc', onClickCallback: () => setSortOrder('asc'), }, diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/TimelineEntry.tsx b/frontend-next-migration/src/preparedPages/AboutPage/ui/TimelineEntry.tsx index c1d29cc20..1d71529f8 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/TimelineEntry.tsx +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/TimelineEntry.tsx @@ -37,8 +37,8 @@ const TimelineEntry = ({ year, text, image, sortOrder, isFirst, isLast }: Timeli />
- {isOpen && ( -
+
+
{image && (
{text}

+ +
+
+ + +
- )} +
); }; From 05cd885fba025211cafb4da2f9f75d002e4a1b11 Mon Sep 17 00:00:00 2001 From: EemeliJ Date: Fri, 21 Aug 2026 18:53:04 +0300 Subject: [PATCH 14/19] Made some fixes to the #line behaviour across resolutions, and chevron placements --- .../AboutPage/ui/About.module.scss | 68 ++++++++++++++++--- .../AboutPage/ui/TimelineEntry.tsx | 22 +++--- 2 files changed, 74 insertions(+), 16 deletions(-) diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss index 00f2be40f..784afa6e3 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss @@ -239,6 +239,12 @@ z-index: 2; } +@media (max-width: breakpoint(sm)) { + .timelineChevron { + left: -14.5px; + } +} + #line::before { content: ''; position: absolute; @@ -251,6 +257,22 @@ border-radius: 999px; } +@media (max-width: breakpoint(sm)) { + .timelineCard::before { + width: 24px; + height: 24px; + left: -33px; + } + + #line::before { + left: 1px; + } + + .timelineChevron { + left: -8.5px; + } +} + #line.timelineNewest::before { top: 40px; bottom: 40px; @@ -505,18 +527,31 @@ } .timelineHeader { + position: relative; + display: flex; justify-content: space-between; align-items: center; + width: 100%; + box-sizing: border-box; + cursor: pointer; user-select: none; min-height: 64px; - padding: 0 16px; + padding: 0; margin-bottom: 0; } +@media (max-width: breakpoint(md)) { + .timelineHeader .yearh1 { + margin: 0; + padding: 0; + line-height: 1; + } +} + .timelineHeader .yearh1::before { display: none; } @@ -628,6 +663,14 @@ z-index: 2; } +@media (max-width: breakpoint(sm)) { + .timelineCard::before { + width: 24px; + height: 24px; + left: -33px; + } +} + .timelineNewest .timelineStart::before { display: none; } @@ -636,17 +679,30 @@ display: none; } -.chevron { +.timelineChevronWrapper { display: flex; align-items: center; justify-content: center; + flex-shrink: 0; - margin-left: 1rem; + width: 24px; height: 24px; transform: rotate(0deg); + transform-origin: center; transition: transform 0.3s ease; +} + +.timelineChevronWrapperOpen { + transform: rotate(180deg); +} + +.chevron { + width: 24px; + height: 24px; + + flex-shrink: 0; img { width: 100%; @@ -654,10 +710,6 @@ } } -.chevronOpen { - transform: rotate(180deg); -} - .aboutContent { width: 100%; display: grid; @@ -722,4 +774,4 @@ font-size: 16px; line-height: 1.2; } -} +} \ No newline at end of file diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/TimelineEntry.tsx b/frontend-next-migration/src/preparedPages/AboutPage/ui/TimelineEntry.tsx index 1d71529f8..f519cbf16 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/TimelineEntry.tsx +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/TimelineEntry.tsx @@ -27,14 +27,20 @@ const TimelineEntry = ({ year, text, image, sortOrder, isFirst, isLast }: Timeli >

{year}

- + + +
From 689d384ba1c7ced8fc44dc50921d313e67b7ea6d Mon Sep 17 00:00:00 2001 From: EemeliJ Date: Sat, 22 Aug 2026 14:05:46 +0300 Subject: [PATCH 15/19] Made some fixes to stat and timeline container width, hooked i18n correctly for sorting dropdown and close button and made some other minor tweaks to dropdown chevron, statcard size and various fonts --- .../AboutPage/ui/About.module.scss | 37 +++++++++++-------- .../src/preparedPages/AboutPage/ui/About.tsx | 11 ++++-- .../AboutPage/ui/TimelineEntry.tsx | 4 +- .../src/shared/i18n/locales/en/about.json | 7 +++- .../src/shared/i18n/locales/fi/about.json | 6 +++ .../ui/DropdownWrapperV2/types/index.ts | 1 + .../ui/DropdownWrapper.module.scss | 2 +- .../DropdownWrapperV2/ui/DropdownWrapper.tsx | 5 ++- 8 files changed, 49 insertions(+), 24 deletions(-) diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss index 784afa6e3..491c6d5e6 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss @@ -9,9 +9,9 @@ border-radius: var(--corner-radius-web); outline: var(--thickness-web) solid var(--black); box-shadow: 10px 10px #121212; - margin-bottom: 5%; + margin-bottom: 2%; padding: 1.2rem; - width: 30%; + width: 40%; box-sizing: border-box; @media (min-width: breakpoint(xxl)) and (max-width: 2004px) { @@ -44,7 +44,7 @@ outline: var(--thickness-web) solid var(--black); box-shadow: 10px 10px #121212; margin-bottom: 5%; - width: 30%; + width: 40%; transition: margin-top 0.3s ease; @@ -72,7 +72,8 @@ .headergrid { display: grid; grid-template-columns: repeat(4, 130px); - gap: 32px; + column-gap: 44px; + row-gap: 32px; padding: 8px 0 0; width: 100%; box-sizing: border-box; @@ -182,7 +183,7 @@ } .sValues { - font: var(--font-sw-xxl); + font: var(--font-sw-l); color: #FFA100; font-weight: 400; line-height: 100%; @@ -293,7 +294,7 @@ } #History { - width: 30%; + width: 40%; font: var(--font-sw-4xl); padding-bottom: var(--spacing-600); text-align: left; @@ -366,7 +367,7 @@ } .sortContainer { - width: 30%; + width: 40%; display: flex; justify-content: flex-end; @@ -439,9 +440,13 @@ box-shadow: none; } -.timelineSortChevron { - width: 16px; - height: 16px; +.timelineSortChevronWrapper { + margin-right: 12px; +} + +.timelineSortHeader :global(img) { + width: 14px !important; + height: 14px !important; } .timelineSortContent { @@ -472,8 +477,8 @@ box-sizing: border-box; - font: var(--font-dm-bold-m); - font-size: 16px; + font: var(--font-dm-m); + font-size: 14px; line-height: 1.2 !important; white-space: nowrap; @@ -609,7 +614,7 @@ box-shadow: 4px 4px #121212; - font: var(--font-dm-bold-s); + font: var(--font-dm-s); cursor: pointer; display: flex; @@ -722,7 +727,7 @@ border-radius: var(--corner-radius-web); padding: 6px 8px; width: 130px; - height: 84px; + height: 92px; box-sizing: border-box; display: flex; @@ -739,7 +744,7 @@ .statItem .gridp { font: var(--font-dm-bold-s); - font-size: 12px; + font-size: 13px; line-height: 1.2; text-align: center; padding: 0; @@ -749,7 +754,7 @@ .statsTitle { font: var(--font-sw-l); - font-size: 22px; + font-size: 26px; color: var(--primary-color); text-align: center; margin: 0 auto; diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx index cc18c50bc..cc6389315 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx @@ -6,6 +6,7 @@ import { useGetMembersCountQuery, useGetDemographicsQuery, getBehindYears } from import { DropdownWrapper } from '@/shared/ui/DropdownWrapperV2/ui/DropdownWrapper'; import Timeline from './Timeline'; import chevronDown from '@/shared/assets/icons/chevronDown.svg'; +import { useClientTranslation } from '@/shared/i18n'; export interface Props { title: string; @@ -44,6 +45,8 @@ const About = (props: Props) => { V2026, } = props; + const { t } = useClientTranslation('about'); + const { data: projectCount = 0, isLoading: membersLoading } = useGetMembersCountQuery(); const { @@ -98,7 +101,7 @@ const About = (props: Props) => {
{ contentClassName={cls.timelineSortContent} contentItemClassName={cls.timelineSortItem} activeItemClassName={cls.timelineSortItemActive} - chevronClassName={cls.timelineSortChevron} + chevronWrapperClassName={cls.timelineSortChevronWrapper} elements={[ { - elementText: 'Uusin', + elementText: t('timeline.newest'), active: sortOrder === 'desc', onClickCallback: () => setSortOrder('desc'), }, { - elementText: 'Vanhin', + elementText: t('timeline.oldest'), active: sortOrder === 'asc', onClickCallback: () => setSortOrder('asc'), }, diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/TimelineEntry.tsx b/frontend-next-migration/src/preparedPages/AboutPage/ui/TimelineEntry.tsx index f519cbf16..404b6b460 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/TimelineEntry.tsx +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/TimelineEntry.tsx @@ -2,6 +2,7 @@ import { useState } from 'react'; import Image, { StaticImageData } from 'next/image'; import chevronDown from '@/shared/assets/icons/chevronDown.svg'; import cls from './About.module.scss'; +import { useClientTranslation } from '@/shared/i18n'; interface TimelineEntryProps { year: number; @@ -14,6 +15,7 @@ interface TimelineEntryProps { const TimelineEntry = ({ year, text, image, sortOrder, isFirst, isLast }: TimelineEntryProps) => { const [isOpen, setIsOpen] = useState(false); + const { t } = useClientTranslation('about'); return (
setIsOpen(false)} > - Sulje + {t('timeline.close')}
diff --git a/frontend-next-migration/src/shared/i18n/locales/en/about.json b/frontend-next-migration/src/shared/i18n/locales/en/about.json index 88dc434d6..1e81817b7 100644 --- a/frontend-next-migration/src/shared/i18n/locales/en/about.json +++ b/frontend-next-migration/src/shared/i18n/locales/en/about.json @@ -5,11 +5,16 @@ "head-keywords": "ALT Zone, ALT Zone mobile game, community game development, participate in game creation, game art mobile game", "og-title": "Psyche's Royale Gaming ry – Creators and Community of ALT Zone Game Art", "og-description": "PRG ry is a Finnish non-profit association developing the ALT Zone mobile game in collaboration with youth, volunteers, and game industry professionals.", - "project": "Has worked on this project", "locality": "Localities represented", "nationality": "Nationalities represented", "behind": "Years into the journey", + "timeline": { + "sort": "Sort by", + "newest": "Newest", + "oldest": "Oldest", + "close": "Close" + }, "story-title": "ALT Zone's history", diff --git a/frontend-next-migration/src/shared/i18n/locales/fi/about.json b/frontend-next-migration/src/shared/i18n/locales/fi/about.json index 72695aee0..50c390752 100644 --- a/frontend-next-migration/src/shared/i18n/locales/fi/about.json +++ b/frontend-next-migration/src/shared/i18n/locales/fi/about.json @@ -9,6 +9,12 @@ "locality": "Eri paikkakunnalta", "nationality": "Eri kansalaisuutta", "behind": "Vuotta matkaa takana", + "timeline": { + "sort": "Järjestä", + "newest": "Uusin", + "oldest": "Vanhin", + "close": "Sulje" + }, "story-title": "ALT Zonen historia", diff --git a/frontend-next-migration/src/shared/ui/DropdownWrapperV2/types/index.ts b/frontend-next-migration/src/shared/ui/DropdownWrapperV2/types/index.ts index cdcefe1f5..3104d29e0 100644 --- a/frontend-next-migration/src/shared/ui/DropdownWrapperV2/types/index.ts +++ b/frontend-next-migration/src/shared/ui/DropdownWrapperV2/types/index.ts @@ -47,4 +47,5 @@ export type DropdownWrapperProps = { headerClassName?: string; childrenWrapperClassName?: string; chevronClassName?: string; + chevronWrapperClassName?: string; }; diff --git a/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.module.scss b/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.module.scss index f04edbd0a..80413ca61 100644 --- a/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.module.scss +++ b/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.module.scss @@ -92,7 +92,7 @@ } .dynamicHeading { - font: var(--font-dm-bold-m); + font: var(--font-dm-m); color: var(--primary-color); cursor: pointer; } diff --git a/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.tsx b/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.tsx index d108a0988..4e84eb52d 100644 --- a/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.tsx +++ b/frontend-next-migration/src/shared/ui/DropdownWrapperV2/ui/DropdownWrapper.tsx @@ -32,6 +32,7 @@ export const DropdownWrapper = (props: DropdownWrapperProps) => { headerClassName = '', childrenWrapperClassName = '', chevronClassName = '', + chevronWrapperClassName = '', } = props; const [isOpen, setIsOpen] = useState(openByDefault || staticDropdown); @@ -207,7 +208,9 @@ export const DropdownWrapper = (props: DropdownWrapperProps) => { {dynamicTitle} {showArrow && ( Date: Tue, 25 Aug 2026 11:17:07 +0300 Subject: [PATCH 16/19] Added a designer block under Timeline with the name as an link to the designers personal page, can be expanded later through i18n. --- .../AboutPage/ui/About.module.scss | 57 ++++++++++++++++++- .../src/preparedPages/AboutPage/ui/About.tsx | 22 +++++++ .../src/shared/i18n/locales/en/about.json | 10 ++++ .../src/shared/i18n/locales/fi/about.json | 10 ++++ 4 files changed, 98 insertions(+), 1 deletion(-) diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss index 491c6d5e6..3df5bc0f8 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss @@ -43,7 +43,7 @@ border-radius: var(--corner-radius-web); outline: var(--thickness-web) solid var(--black); box-shadow: 10px 10px #121212; - margin-bottom: 5%; + margin-bottom: 30px; width: 40%; transition: margin-top 0.3s ease; @@ -69,6 +69,60 @@ } } +.designerContainer { + width: 40%; + box-sizing: border-box; + + background: var(--background); + border-radius: var(--corner-radius-web); + outline: var(--thickness-web) solid var(--black); + box-shadow: 10px 10px #121212; + + padding: 16px 16px; + + position: relative; + top: -22px; + + display: flex; + justify-content: center; + align-items: center; + + @media (max-width: breakpoint(xxl)) { + width: 50%; + } + + @media (max-width: breakpoint(xl)) { + width: 50%; + } + + @media (max-width: breakpoint(md)) { + width: 70%; + } + + @media (max-width: breakpoint(sm)) { + width: 90%; + } +} + +.designerText { + font: var(--font-dm-m); + font-size: 16px; + line-height: 1.2; + color: var(--white); + text-align: center; + margin: 0; + + span { + color: var(--primary-color); + } + + @media (max-width: breakpoint(sm)) { + span { + display: block; + } + } +} + .headergrid { display: grid; grid-template-columns: repeat(4, 130px); @@ -598,6 +652,7 @@ height: 4px; background: #FFA100; margin-bottom: var(--spacing-400); + border-radius: 999px; } .timelineCloseButton { diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx index cc6389315..ddc4a11ad 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx @@ -47,6 +47,10 @@ const About = (props: Props) => { const { t } = useClientTranslation('about'); + const designers = t('designer.names', { + returnObjects: true, + }) as { name: string; url: string }[]; + const { data: projectCount = 0, isLoading: membersLoading } = useGetMembersCountQuery(); const { @@ -152,6 +156,24 @@ const About = (props: Props) => { />
+ +
+

+ {t('designer.label')}{' '} + {designers.map((designer, index) => ( + + {index > 0 && ', '} + + {designer.name} + + + ))} +

+
); diff --git a/frontend-next-migration/src/shared/i18n/locales/en/about.json b/frontend-next-migration/src/shared/i18n/locales/en/about.json index 1e81817b7..4fd7f544d 100644 --- a/frontend-next-migration/src/shared/i18n/locales/en/about.json +++ b/frontend-next-migration/src/shared/i18n/locales/en/about.json @@ -16,6 +16,16 @@ "close": "Close" }, + "designer": { + "label": "Designed by:", + "names": [ + { + "name": "Sanna-Kaisa Mastokangas", + "url": "https://piisamisiili.artstation.com/" + } + ] +}, + "story-title": "ALT Zone's history", "V2019": "The idea for the ALT Zone mobile game was born from the imagination of an 11-year-old boy. The concept was introduced to Psyche’s Royale Gaming (PRG ry), whose team immediately got excited—this was a unique kind of game we’d want to play ourselves and share with others! What stood out to the PRG team was that instead of relying on the typical fighting and shooting mechanics found in mobile games, ALT Zone’s gameplay would revolve around shielding and defense. These forms of protection would be drawn from real life: the alcoholic’s shield might be a bottle, the know-it-all’s a book, and so on.\n\nSoon, a whole cluster of new, thought-provoking ideas began to take shape around the concept. This raised a question: could the game function as a platform for artistic exploration and reflection on the human condition? What kind of discussion could we spark? At the time, PRG ry was primarily organizing inclusive gaming events and eSports activities, which had already built ties to the professional gaming community. We began pitching the game idea to a number of industry professionals—and they seemed just as excited as we were.\n\nInspired by this, the PRG team began to think more broadly about the potential of games as thought-provoking art—and about the role of games in arts education more generally. How could games be meaningfully integrated into schools alongside theatre, visual arts, literature, and cinema? In arts education, beyond exploring historical movements and examples, a key element often lies in collectively experiencing an artwork and reflecting on its meanings together. But how could this be realized through the medium of games?\n\nIn conversations with game industry professionals, we learned that most of their attempts to introduce game testing in schools had failed—mainly because schools don’t engage in commercial collaborations. PRG ry recognized a chance to bring its expertise in arts education to meet a clear need in the games sector. Before shifting toward game development, PRG had been touring theatre performances in schools—fulfilling a national curriculum requirement to provide all students yearly opportunities to experience and participate in theatre. These performances were followed by classroom-based workshops and discussions.\n\nA new question emerged: what if we brought the ALT Zone mobile game to schools in a similar format, and brought a unified gameplay experience into the classroom—something students could engage with as a shared artistic encounter, either as a singular piece or as part of a broader thematic whole, also enabling the possibility to take part in its further development. After all, PRG ry is a volunteer-based association where people bring their ideas to life without money ever changing hands. The idea felt too compelling to ignore, and a network of professional developers offered to help us figure out how to move forward. It was time to take a leap into the unknown. The work could begin.", diff --git a/frontend-next-migration/src/shared/i18n/locales/fi/about.json b/frontend-next-migration/src/shared/i18n/locales/fi/about.json index 50c390752..042026b70 100644 --- a/frontend-next-migration/src/shared/i18n/locales/fi/about.json +++ b/frontend-next-migration/src/shared/i18n/locales/fi/about.json @@ -16,6 +16,16 @@ "close": "Sulje" }, + "designer": { + "label": "Sivun suunnitteli:", + "names": [ + { + "name": "Sanna-Kaisa Mastokangas", + "url": "https://piisamisiili.artstation.com/" + } + ] +}, + "story-title": "ALT Zonen historia", "V2019": "ALT Zone -mobiilipelin idea syntyi 11-vuotiaan pojan toimesta. Idea esiteltiin Psyche's Royale Gamingille (PRG ry), jonka tiimi innostui ideasti heti – tässä olisi uniikki peli, jota haluamme itse pelata ja esitellä myös muille! Pelin ideassa PRG-tiimiä kiinnosti erityisesti se, että mobiilipeleille tyypillisten taistelu- ja ammuntamekanismien sijaan pelimekaniikka keskittyisi suojautumiseen. Nämä suojautumistavat olisivat kuin elävästä elämästä: alkoholistilla suojakilpenä toimisi pullo, viisastelijalla kirja, ja niin edelleen.\n\n Pelikonseptin ympärille alkoi pian muodostua kokonainen joukko uusia, ajatuksia herättäviä ideoita. Tämä herätti kysymyksen: voisiko peli toimia alustana taiteelliselle tutkimiselle ja pohdinnalle ihmisyyden kokemuksesta? Minkälaista keskustelua pelin välityksellä voitaisiin synnyttää? Tuohon aikaan, PRG ry toimi ensisijaisesti osallistavien pelitapahtumien ja eSports-toiminnan järjestäjänä, joiden kautta yhdistyksellä oli jo ennestään yhteys pelialan toimijoiden ammattiyhteisöön. Esittelimme pelin ideaa useammalle pelialan ammattilaiselle, jotka tuntuivat innostuvan samalla lailla kuin mekin.\n\n Tämän innoittamana PRG:n tiimi alkoi pohtia laajemmin pelien potentiaalia ajatuksia herättävänä taiteena– ja pelien roolia taidekasvatuksessa ylipäätään. Miten pelit voitaisiin tuoda kouluympäristöön mielekkäällä tavalla, teatterin, kuvataiteen, kirjallisuuden ja elokuvan rinnalle? Taidekasvatuksessa olennaista ei ole ainoastaan historiallisten suuntausten ja esimerkkien käsittely, vaan myös yhteisen teoksen kokeminen ja sen merkitysten tarkasteleminen yhdessä. Mutta miten tämä voisi toteutua pelien kohdalla?\n\n Pelialan osaajien kanssa käydyissä keskusteluissa kävi ilmi, että heidän yrityksensä saada kouluille pelitestauksia olivat pääasiassa epäonnistuneet, sillä koulut eivät tee kaupallista yhteistyötä. PRG ry näki tässä mahdollisuuden yhdistää taidekasvatuksellisen osaamisensa pelialan tarpeisiin. Ennen kuin PRG ry vaihtoi toimialansa peleihin, se nimittäin vei teatteriesityksiä kouluihin, täytten kouluissa lakisääteisen opetussuunnitelmaan kuuluvan pykälän, jossa jokaiselle oppilaalle oli vuosittain tarjottava mahdollisuus teatteritaiteen kokemiseen ja tekemiseen. Tätä tehtiin tuottamalla näytelmiä, joita käsiteltiin työpajatyyppisesti luokissa esityksen jälkeen.\n\n Kysymys oli valmis: mitä jos veisimme ALT Zone -mobiilipelin kouluille tällä samalla sapluunalla, ja toisimme luokkahuoneeseen yhtenäisen pelikokemuksen, jota oppilaat voisivat tarkastella ohjatusti taide-elämyksenä - ja halutessaan osallistua sen jatkokehittämiseen? PRG ry on kuitenkin yhdistysmuotoinen vapaaehtoistyön osaaja, jossa ihmiset ovat tehneet unelmistaan totta ilman että raha vaihtaa omistajaa.Idea tuntui liian hyvälltä sivuutettavaksi, ja pelialan ammattilaisten verkosto lupasi auttaa matkalla oikeiden asioiden pohtimisessa. Oli otettava hyppy tuntemattomaan; työ saattoi alkaa.", From 856282da7f9f5cc8b6022d2e4f660d9240cf9c01 Mon Sep 17 00:00:00 2001 From: EemeliJ Date: Tue, 25 Aug 2026 11:41:16 +0300 Subject: [PATCH 17/19] Small fix to the close button in timeline entries, now it scales accordingly to thinner viewport widths --- .../src/preparedPages/AboutPage/ui/About.module.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss index 3df5bc0f8..8f0c8486d 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss @@ -677,6 +677,10 @@ justify-content: center; box-sizing: border-box; + + @media (max-width: 400px) { + width: calc(100% - 16px); + } } .timelineCard { From 325374c6d8302b00036f8ae5a56e825b7cee4b44 Mon Sep 17 00:00:00 2001 From: EemeliJ Date: Tue, 1 Sep 2026 11:13:20 +0300 Subject: [PATCH 18/19] Removed some unused props from about.tsx --- .../src/preparedPages/AboutPage/ui/About.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx index ddc4a11ad..7b5303fea 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.tsx @@ -10,8 +10,6 @@ import { useClientTranslation } from '@/shared/i18n'; export interface Props { title: string; - description: string; - keywords: string; storytitle: string; project: string; locality: string; From 9ba2665fd6800a6c65059748f24d277c1bf841d2 Mon Sep 17 00:00:00 2001 From: EemeliJ Date: Fri, 11 Sep 2026 17:05:45 +0300 Subject: [PATCH 19/19] Added more spacing between timeline and designer containers, and set up the page to open the first timeline entry when loaded --- .../src/preparedPages/AboutPage/ui/About.module.scss | 2 +- .../src/preparedPages/AboutPage/ui/TimelineEntry.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss index 8f0c8486d..e92e3b310 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/About.module.scss @@ -81,7 +81,7 @@ padding: 16px 16px; position: relative; - top: -22px; + top: -12px; display: flex; justify-content: center; diff --git a/frontend-next-migration/src/preparedPages/AboutPage/ui/TimelineEntry.tsx b/frontend-next-migration/src/preparedPages/AboutPage/ui/TimelineEntry.tsx index 404b6b460..7646cd70b 100644 --- a/frontend-next-migration/src/preparedPages/AboutPage/ui/TimelineEntry.tsx +++ b/frontend-next-migration/src/preparedPages/AboutPage/ui/TimelineEntry.tsx @@ -14,7 +14,7 @@ interface TimelineEntryProps { } const TimelineEntry = ({ year, text, image, sortOrder, isFirst, isLast }: TimelineEntryProps) => { - const [isOpen, setIsOpen] = useState(false); + const [isOpen, setIsOpen] = useState(isFirst); const { t } = useClientTranslation('about'); return (