diff --git a/frontend/apps/service-site/.storybook/main.ts b/frontend/apps/service-site/.storybook/main.ts index 367146994..381595d4f 100644 --- a/frontend/apps/service-site/.storybook/main.ts +++ b/frontend/apps/service-site/.storybook/main.ts @@ -16,6 +16,7 @@ const config: StorybookConfig = { } return config }, + staticDirs: ["../public"], } export default config diff --git a/frontend/apps/service-site/src/app/layout.tsx b/frontend/apps/service-site/src/app/layout.tsx index 2afddb601..d5c6a47a6 100644 --- a/frontend/apps/service-site/src/app/layout.tsx +++ b/frontend/apps/service-site/src/app/layout.tsx @@ -1,4 +1,4 @@ -import type { Metadata } from 'next' +import type { Metadata, Viewport } from 'next' import '@/styles/globals.css' import { Footer, Header } from '@/components' import type { Lang } from '@/features/i18n' @@ -9,6 +9,11 @@ export const metadata: Metadata = { description: 'Liam blog', } +export const viewport: Viewport = { + width: 'device-width', + initialScale: 1, +} + export default function RootLayout({ children, params: { lang }, diff --git a/frontend/apps/service-site/src/components/Footer/Footer.module.css b/frontend/apps/service-site/src/components/Footer/Footer.module.css index c64303105..0301dad39 100644 --- a/frontend/apps/service-site/src/components/Footer/Footer.module.css +++ b/frontend/apps/service-site/src/components/Footer/Footer.module.css @@ -8,6 +8,18 @@ align-items: flex-end; } +@media screen and (max-width: 1024px) { + .footer { + padding: var(--spacing-10, 40px); + } +} + +@media screen and (max-width: 768px) { + .footer { + padding: var(--spacing-10, 40px) var(--spacing-4, 16px) var(--spacing-8, 32px); + } +} + .logoContainer { display: flex; flex-direction: column; @@ -15,6 +27,13 @@ gap: var(--spacing-4, 16px); } +@media screen and (max-width: 768px) { + .logoContainer { + padding-bottom: var(--spacing-2, 8px); + } +} + + .text { color: var(--foreground); font-family: var(--main-font); diff --git a/frontend/apps/service-site/src/components/Header/Header.module.css b/frontend/apps/service-site/src/components/Header/Header.module.css index 2ffa3be1e..bc617015a 100644 --- a/frontend/apps/service-site/src/components/Header/Header.module.css +++ b/frontend/apps/service-site/src/components/Header/Header.module.css @@ -3,9 +3,26 @@ height: var(--default-header-height); padding: 0 var(--default-page-margin); margin: 0 auto; - background-color: var(--background); + background-color: var(--global-background); display: flex; align-items: center; + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1000; +} + +@media screen and (max-width: 1024px) { + .header { + padding: 0 var(--spacing-10, 40px); + } +} + +@media screen and (max-width: 768px) { + .header { + padding: 0 var(--spacing-4, 16px); + } } .h1 { diff --git a/frontend/apps/service-site/src/components/TopCards/TopCards.module.css b/frontend/apps/service-site/src/components/TopCards/TopCards.module.css index 449dc2517..0e8c26b37 100644 --- a/frontend/apps/service-site/src/components/TopCards/TopCards.module.css +++ b/frontend/apps/service-site/src/components/TopCards/TopCards.module.css @@ -1,8 +1,24 @@ .topCards { + --card-padding: var(--spacing-6, 24px); + --large-card-margin: var(--spacing-20, 80px); + --tablet-card-padding: var(--spacing-5, 20px); + --tablet-large-card-padding: var(--spacing-10, 40px); + --mobile-card-padding: var(--spacing-2, 8px); + --mobile-large-card-padding: var(--spacing-4, 16px); + display: grid; grid-template-columns: repeat(4, 1fr); grid-template-rows: repeat(4, auto); gap: 0; + padding: 0 56px; +} + +@media screen and (max-width: 1024px) { + .topCards { + grid-template-columns: repeat(2, 1fr); + grid-template-rows: auto; + padding: 0; + } } .topCards > * { @@ -11,17 +27,22 @@ } .topCard { - padding: 24px; - gap: 16px; + padding: var(--card-padding); + gap: var(--spacing-4, 16px); display: flex; flex-direction: column; color: var(--global-foreground); - transition: color 0.1s ease-out; + transition: color 0.1s ease-out, background-color 0.1s ease-out; +} + +.topCard:hover { + color: var(--global-background); + background-color: var(--global-foreground); +} - &:hover { - color: var(--global-background); - background-color: var(--global-foreground); - transition: color 0.1s ease-out; +@media screen and (max-width: 768px) { + .topCard { + padding: var(--mobile-card-padding); } } @@ -37,13 +58,24 @@ display: none; } -.tag { +.tag, +.writer { font-family: var(--message-font); font-size: var(--font-size-6, 16px); - font-weight: 500; line-height: 120%; } +@media screen and (max-width: 768px) { + .tag, + .writer { + font-size: var(--font-size-3, 12px); + } +} + +.tag { + font-weight: 500; +} + .title { font-size: var(--font-size-10, 24px); font-weight: 400; @@ -51,11 +83,14 @@ font-family: var(--message-font); } +@media screen and (max-width: 768px) { + .title { + font-size: var(--font-size-5, 14px); + } +} + .writer { - font-size: var(--font-size-6, 16px); font-weight: 400; - line-height: 120%; - font-family: var(--message-font); align-items: flex-end; } @@ -70,93 +105,242 @@ object-fit: cover; } -.topCards > :not(:first-child):not(:nth-child(2)) { - .title { - line-clamp: 4; - -webkit-line-clamp: 4; - display: -webkit-box; - -webkit-box-orient: vertical; - overflow: hidden; - height: calc(var(--font-size-10, 24px) * 4 * 1.2); - } +/* Title line limit */ +.topCards > :not(:first-child):not(:nth-child(8)) .title { + --line-height: 1.2; + --max-lines: 4; + + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: var(--max-lines); + line-clamp: var(--max-lines); + overflow: hidden; + height: calc(var(--font-size-10, 24px) * var(--max-lines) * var(--line-height)); } -.topCards > :first-child, -.topCards > :nth-child(2) { - .textContainer { - margin: var(--spacing-20, 80px) 0; - justify-content: flex-start; - } +/* Styles for first and eighth cards */ +.topCards > :is(:first-child, :nth-child(8)) .textContainer { + margin: var(--large-card-margin) 0; + justify-content: flex-start; +} - .tags { - display: flex; - gap: var(--spacing-3, 12px); - } +.topCards > :is(:first-child, :nth-child(8)) .tags { + display: flex; + gap: var(--spacing-3, 12px); + flex-wrap: wrap; +} - .title { - font-size: var(--font-size-18, 48px); +.topCards > :is(:first-child, :nth-child(8)) .title { + font-size: var(--font-size-18, 48px); + font-weight: 400; + line-height: 120%; + -webkit-line-clamp: 4; + line-clamp: 4; + display: -webkit-box; + -webkit-box-orient: vertical; + overflow: hidden; + height: auto; +} - font-weight: 400; - line-height: 120%; - line-clamp: 4; - -webkit-line-clamp: 4; - display: -webkit-box; - -webkit-box-orient: vertical; - overflow: hidden; - height: auto; - } +.topCards > :is(:first-child, :nth-child(8)) .introduction { + margin-top: var(--spacing-10, 40px); + display: block; + width: 50%; + font-family: var(--main-font); + font-size: var(--s-fontSize-5, 14px); + font-weight: 400; + line-height: 160%; + border-left: 1px solid var(--global-foreground); + padding-left: var(--spacing-4, 16px); + margin-left: 50%; +} - .introduction { - margin-top: 40px; - display: block; - width: 50%; - font-family: var(--main-font); - font-size: var(--s-fontSize-5, 14px); +.topCards > :is(:first-child, :nth-child(8)):hover .introduction { + border-color: var(--global-background); +} + +@media screen and (max-width: 1024px) { + .topCards > :is(:first-child, :nth-child(8)) .introduction { + margin-top: var(--spacing-6, 24px); + } +} - font-weight: 400; - line-height: 160%; - border-left: 1px solid var(--global-foreground); - padding-left: var(--spacing-4, 16px); - margin-left: 50%; +@media screen and (max-width: 768px) { + .topCards > :is(:first-child, :nth-child(8)) .title { + font-size: var(--font-size-8, 16px); } - &:hover { - .introduction { - border-color: var(--global-background); - } + .topCards > :is(:first-child, :nth-child(8)) .introduction { + margin-top: 0; + width: 100%; + margin-left: 0; + font-size: var(--font-size-3, 12px); } } +/* Grid layout for the first card */ .topCards > :first-child { grid-column: span 2; grid-row: span 3; + padding-left: 0; +} + +.topCards > :first-child .topCard { + padding-left: 0; + margin-left: -56px; + gap: 0; } -.topCards > :nth-child(2) { +.topCards > :first-child .textContainer { + padding-left: var(--large-card-margin); +} + +/* Grid layout for the eighth card */ +.topCards > :nth-child(8) { order: 14; grid-column: 3 / span 2; grid-row: 4 / span 3; - margin-right: calc(-1 * var(--spacing-20, 80px)); + padding-right: 0; } -.topCards > :first-child { +.topCards > :nth-child(8) .topCard { + padding-right: 0; + margin-right: -56px; + gap: 0; +} + +.topCards > :nth-child(8) .textContainer { + padding-right: var(--large-card-margin); +} + +/* Tablet styles */ +@media screen and (max-width: 1024px) { .topCard { - padding-left: 0; - margin-left: calc(-1 * var(--spacing-20, 80px)); + padding: var(--tablet-card-padding); } .textContainer { - padding-left: var(--spacing-20, 80px); + flex-grow: 0; + } + + .topCards > :not(:first-child):not(:nth-child(8)) .title { + --max-lines: auto; + } + + /* Adjust left and right padding for cards */ + .topCards > :nth-child(2n):not(:nth-child(8)):nth-child(-n+7), + .topCards > :nth-child(2n+9) { + padding-left: var(--tablet-card-padding); + } + + .topCards > :nth-child(2n+1):not(:first-child):nth-child(-n+7), + .topCards > :nth-child(2n+10) { + padding-right: var(--tablet-card-padding); + } + + .topCards > :nth-child(8) { + margin-left: 0; + margin-right: 0; + } + + /* Adjust layout for first and eighth cards */ + .topCards > :is(:first-child, :nth-child(8)) { + grid-column: span 2; + grid-row: auto; + } + + .topCards > :is(:first-child, :nth-child(8)) .textContainer { + margin: var(--tablet-large-card-padding) 0; + } + + .topCards > :first-child .topCard { + padding-left: 0; + margin-left: 0; + padding-right: var(--tablet-large-card-padding); + } + + .topCards > :first-child .textContainer { + padding-left: var(--tablet-large-card-padding); + } + + .topCards > :nth-child(8) { + order: initial; + margin-right: 0; + } + + .topCards > :nth-child(8) .topCard { + padding-right: 0; + margin-right: 0; + padding-left: var(--tablet-large-card-padding); + } + + .topCards > :nth-child(8) .textContainer { + padding-right: var(--tablet-large-card-padding); } } -.topCards > :nth-child(2) { +/* Mobile styles */ +@media screen and (max-width: 768px) { .topCard { - padding-right: 0; - margin-right: calc(-1 * var(--spacing-20, 80px)); + padding: var(--spacing-4, 16px) var(--mobile-card-padding); } .textContainer { - padding-right: var(--spacing-20, 80px); + flex-grow: 0; + } + + .topCards > :not(:first-child):not(:nth-child(8)) .title { + --max-lines: auto; + } + + /* Adjust left and right padding for cards */ + .topCards > :nth-child(2n):not(:nth-child(8)):nth-child(-n+7), + .topCards > :nth-child(2n+9) { + padding-left: var(--mobile-card-padding); + } + + .topCards > :nth-child(2n+1):not(:first-child):nth-child(-n+7), + .topCards > :nth-child(2n+10) { + padding-right: var(--mobile-card-padding); + } + + .topCards > :nth-child(8) { + margin-left: 0; + margin-right: 0; + } + + /* Adjust layout for first and eighth cards */ + .topCards > :is(:first-child, :nth-child(8)) { + grid-column: span 2; + grid-row: auto; + } + + .topCards > :is(:first-child, :nth-child(8)) .textContainer { + margin: var(--mobile-large-card-padding) 0; + } + + .topCards > :first-child .topCard { + padding-left: 0; + margin-left: 0; + padding-right: var(--mobile-large-card-padding); + } + + .topCards > :first-child .textContainer { + padding-left: var(--mobile-large-card-padding); + } + + .topCards > :nth-child(8) { + order: initial; + margin-right: 0; + } + + .topCards > :nth-child(8) .topCard { + padding-right: 0; + margin-right: 0; + padding-left: var(--mobile-large-card-padding); + } + + .topCards > :nth-child(8) .textContainer { + padding-right: var(--mobile-large-card-padding); } } diff --git a/frontend/apps/service-site/src/components/TopCards/TopCards.stories.tsx b/frontend/apps/service-site/src/components/TopCards/TopCards.stories.tsx index 1ecd804cd..158fef43f 100644 --- a/frontend/apps/service-site/src/components/TopCards/TopCards.stories.tsx +++ b/frontend/apps/service-site/src/components/TopCards/TopCards.stories.tsx @@ -1,12 +1,49 @@ import type { Meta, StoryObj } from '@storybook/react' import { TopCards } from './' +import { DocumentContentType } from 'contentlayer2/source-files'; + +const commonPostData = (postId: number) => ({ + id: `${postId}`, + title: `${postId} The No-Code Revolution: A New Era Where Non-Programmers Can Build Apps`, + tags: ['Democratization', 'Empowerment', 'Innovation', 'Agility', 'Disruption'], + writer: 'Aiden Sparks', + introduction: "The rise of no-code application platforms is changing the landscape of software development. These platforms allow users with little to no coding knowledge to create fully functional applications, reducing the barrier to entry for innovation. The convenience, flexibility, and speed offered by no-code tools are helping businesses of all sizes innovate at unprecedented rates.", + image: `/images/posts/${postId}/image.png`, + _id: `${postId}`, + date: new Date().toISOString(), + categories: ['Technology', 'Business'], + body: { + type: 'mdx', + code: '', + content: "No-code platforms are revolutionizing the way we think about software development. By enabling non-programmers to build applications, they are democratizing access to technology and fostering innovation across various sectors.", + raw: '', + html: '' + }, + lang: 'en', + slug: `dummy-${postId}`, +}); const meta = { component: TopCards, args: { - posts: [], - }, + posts: Array.from({ length: 14 }, (_, i) => ({ + ...commonPostData(i + 1), + _raw: { + sourceFilePath: '', + sourceFileName: '', + sourceFileDir: '', + contentType: 'post' as DocumentContentType, + flattenedPath: '' + }, + type: 'Post' as const, + body: { + ...commonPostData(i + 1).body, + raw: '', + html: '', + } + })) + } } satisfies Meta export default meta diff --git a/frontend/apps/service-site/src/styles/globals.css b/frontend/apps/service-site/src/styles/globals.css index 9b59551b5..077f6dd76 100644 --- a/frontend/apps/service-site/src/styles/globals.css +++ b/frontend/apps/service-site/src/styles/globals.css @@ -1,8 +1,11 @@ -@import url('destyle.css'); -@import url('./variables.css'); +@import 'destyle.css'; +@import './variables.css'; +@import './variables.tokens-dark.css'; +@import './variables.tokens-typography.css'; -@import url('./variables.tokens-dark.css'); -@import url('./variables.tokens-typography.css'); +:root { + --main-padding: var(--spacing-10, 40px); +} body { color: var(--global-foreground); @@ -14,5 +17,12 @@ body { main { max-width: var(--default-page-width-with-margin); margin: 0 auto; - padding: var(--spacing-10, 40px) var(--spacing-20, 80px); + padding: var(--main-padding) 0; + margin-top: var(--default-header-height); +} + +@media screen and (max-width: 1024px) { + main { + padding: 0; + } }