Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d52b3b1
Reapply "feat: enable light mode (#255)" (#443)
marcin-hoa May 13, 2025
956f02b
feat: light mode improvements
marcin-hoa May 19, 2025
c8ace7e
chore: clean-up
marcin-hoa May 19, 2025
38ca2d3
fix: hamburger-button color
marcin-hoa May 19, 2025
e97641f
feat: added theme switcher - wip
marcin-hoa May 19, 2025
9329dcd
feat: added theme switch and fixed cookies consent logic
marcin-hoa May 29, 2025
0445dc3
feat: added theme in gh-giscus component
marcin-hoa May 29, 2025
e0a2694
feat: enhance theming support with light/dark variants and gradient o…
marcin-hoa Jun 4, 2025
5fccbda
fix: typo
marcin-hoa Jun 5, 2025
e957263
fix(client): fixed issue with author card in article
DamianBrzezinskiHoA Aug 12, 2025
18a4c96
fix(client): fixed issue after rebasing to main
DamianBrzezinskiHoA Aug 12, 2025
097903d
feat(client): appended lightmode for roadmap section
DamianBrzezinskiHoA Aug 14, 2025
44ba780
feat(client): changed roadmap lightmode
DamianBrzezinskiHoA Aug 19, 2025
102f365
feat(client): changed primary tile color
DamianBrzezinskiHoA Aug 20, 2025
c04b42c
feat: add footer light mode support
dmaduzia Oct 2, 2025
6f217ab
Merge branch 'main' into feature/light-mode
dmaduzia Oct 2, 2025
564f72d
fix: update code block background color
dmaduzia Oct 2, 2025
7d2e0d2
fix: prevent images from jumping when switching theme
dmaduzia Oct 2, 2025
1bc2613
feat: use dark mode as default
dmaduzia Oct 3, 2025
2764f48
chore: update roadmap colors
dmaduzia Oct 3, 2025
0323183
Merge branch 'main' into feature/light-mode
dmaduzia Oct 6, 2025
e7281ca
chore: pr cleanup
dmaduzia Oct 6, 2025
4b86026
chore: remove unused import
dmaduzia Oct 6, 2025
169c1cd
refactor: address pr comments
dmaduzia Oct 7, 2025
6013a04
refactor: address pr comment
dmaduzia Oct 7, 2025
097b62d
feat: footer tweaks (#476)
dmaduzia Oct 15, 2025
1a57dc8
fix: address ai comments
dmaduzia Oct 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added apps/blog/src/assets/HOA_logo_blue.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/blog/src/assets/HOA_logo_white.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 16 additions & 3 deletions apps/blog/src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@
"recommended": "Recommended Articles",
"angularNews": "Angular News",
"guides": "Guides",
"partners": "Community partners",
"mainPartner": "Main partner",
"angularInDepth": "Angular In Depth",
"articleTypes": "Article types",
"categories": {
"all": "Latest",
"news": "News",
Expand All @@ -24,15 +23,27 @@
"home": "Home Page",
"about": "About us",
"meetups": "Angular Meetups",
"guides": "Angular Guides",
"news": "Angular News",
"in_depth": "In-Depth articles",
"partnership": "Let's discuss partnership",
"become_author": "Become an author",
"newsletter": "Angular.letter",
"navLinks": "Navigation links",
"roadmap": "Roadmap",
"toggle_theme": "Toggle theme",
"open_search_dialog": "Open search dialog",
"languagePicker": {
"pl": "Polish",
"en": "English"
"en": "English",
"select_lang": "Select language"
}
},
"footer": {
"mainPartner": "Main partner",
"partners": "Community partners",
"al-description": "This is the place where you can learn Angular, discover best practices, and stay updated with the latest news and trends"
},
"authorPage": {
"posted_by": "Posted by {{name}}"
},
Expand Down Expand Up @@ -203,6 +214,7 @@
"registerButtonShortened": "Download"
},
"search": {
"title": "Search articles",
"no-matches": "No recent matches",
"results": "{{total}} results"
},
Expand All @@ -218,6 +230,7 @@
"changeLangToEnglish": "Change language to English"
},
"socialLinks": {
"title": "Social media",
"facebook": "Like our Facebook page",
"twitter-x": "Follow us on X (formerly Twitter)",
"linkedIn": "Follow us on LinkedIn",
Expand Down
19 changes: 16 additions & 3 deletions apps/blog/src/assets/i18n/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@
"recommended": "Polecane Artykuły",
"angularNews": "Wiadomości ze świata Angulara",
"guides": "Poradniki",
"partners": "Community partnerzy",
"mainPartner": "Główny partner",
"angularInDepth": "Angular In Depth",
"articleTypes": "Typy artykułów",
"categories": {
"all": "Najnowsze",
"news": "Nowości",
Expand All @@ -24,15 +23,27 @@
"home": "Strona Główna",
"about": "O nas",
"meetups": "Angular Meetups",
"guides": "Angular Guides",
"news": "Angular News",
"in_depth": "Artykuły In-Depth",
"partnership": "Porozmawiajmy o partnerstwie",
"become_author": "Zostań autorem",
"newsletter": "Angular.letter",
"navLinks": "Menu",
"roadmap": "Roadmap",
"toggle_theme": "Przełącz motyw",
"open_search_dialog": "Otwórz okno wyszukiwania",
"languagePicker": {
"pl": "Polski",
"en": "Angielski"
"en": "Angielski",
"select_lang": "Wybierz język"
}
},
"footer": {
"partners": "Community partnerzy",
"mainPartner": "Główny partner",
"al-description": "To jest miejsce, w którym możesz nauczyć się Angulara, odkrywać najlepsze praktyki i być na bieżąco z najnowszymi wiadomościami i trendami"
},
Comment on lines +42 to +46
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Polish wording: replace “Community partnerzy”.

Line 43 mixes English and Polish. Please change it to a fully Polish form such as “Partnerzy społeczności”, so the footer copy stays consistent.

🤖 Prompt for AI Agents
In apps/blog/src/assets/i18n/pl.json around lines 42 to 46, the value for the
"partners" key on line 43 mixes English and Polish; replace "Community
partnerzy" with the fully Polish phrasing Partnerzy społeczności so the footer
text is consistently Polish.

"home": {
"latest": "Latest articles"
},
Expand Down Expand Up @@ -206,6 +217,7 @@
"registerButtonShortened": "Pobierz"
},
"search": {
"title": "Wyszukiwanie artykułów",
"no-matches": "Brak wyników",
"results": "{{total}} wyników"
},
Expand All @@ -221,6 +233,7 @@
"changeLangToEnglish": "Zmień język na angielski"
},
"socialLinks": {
"title": "Media społecznościowe",
"facebook": "Polub nas na Facebooku",
"twitter-x": "Obserwuj nas na X (dawniej Twitter)",
"linkedIn": "Obserwuj nas na LinkedInie",
Expand Down
2 changes: 1 addition & 1 deletion apps/blog/src/assets/icons/arrow-down.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions apps/blog/src/assets/icons/moon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions apps/blog/src/assets/icons/sun.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<ng-container *transloco="let t; read: 'aboutUsPage'">
<h2 class="py-4 text-[40px] font-bold">
<h2 class="text-al-primary-foreground py-4 text-[40px] font-bold">
{{ t('title') }}
</h2>

<al-card>
<al-card class="bg-transparent">
<section
alCardContent
aria-labelledby="angular-love"
Expand Down Expand Up @@ -35,13 +35,18 @@ <h2 class="py-4 text-[40px] font-bold">
<al-newsletter alCardContent />
</al-card>

<h2 class="mb-8 mt-10 text-[40px] font-bold">
<h2 class="text-al-primary-foreground mb-8 mt-10 text-[40px] font-bold">
{{ t('authorsTitle') }}
</h2>
</ng-container>

@for (author of authorsCards(); track author.slug) {
<al-author-card class="mb-6 block" [author]="author" [linkable]="true" />
<al-author-card
class="mb-6 block"
[author]="author"
[linkable]="true"
[hideGradient]="hideGradientInAuthorCards()"
/>

@if ($index === noAuthorsInView() - 2) {
@defer (on viewport) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
} from '@angular-love/blog/shared/ui-card';
import { InfiniteScrollTriggerDirective } from '@angular-love/blog/shared/ui-pagination';
import { SocialMediaIconsComponent } from '@angular-love/blog/shared/ui-social-media-icons';
import { AppThemeStore } from '@angular-love/data-access-app-theme';

@Component({
selector: 'al-about-us',
Expand All @@ -41,6 +42,12 @@ export class FeatureAboutUsComponent implements OnInit {
return this.authorsCards()?.length || 0;
});

private readonly _theme = inject(AppThemeStore).theme;

readonly hideGradientInAuthorCards = computed(
() => this._theme() === 'light',
);

private readonly _skip = this._authorListStore.skip;
private readonly _total = this._authorListStore.total;
private readonly _pageSize = this._authorListStore.pageSize;
Expand Down
42 changes: 26 additions & 16 deletions libs/blog/app-theme/data-access-app-theme/src/app-theme.store.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { isPlatformBrowser } from '@angular/common';
import { inject, Injectable, PLATFORM_ID } from '@angular/core';
import { signalStore, withMethods, withState } from '@ngrx/signals';
import { patchState, signalStore, withMethods, withState } from '@ngrx/signals';

type Theme = 'dark' | 'light';
export type Theme = 'dark' | 'light';

interface AppThemeStore {
theme: Theme;
}

export const AppThemeStore = signalStore(
{ providedIn: 'root' },
withState<AppThemeStore>({ theme: 'light' }),
withState<AppThemeStore>({ theme: 'dark' }),
withMethods(
(
store,
Expand All @@ -19,31 +19,41 @@ export const AppThemeStore = signalStore(
) => ({
syncWithSystemTheme: () => {
if (isPlatformBrowser(platformId)) {
ccConsumer.setThemeClass(getSystemTheme());
const theme =
(localStorage.getItem('theme') as Theme) ?? getSystemTheme();
ccConsumer.setThemeAttribute(theme);
patchState(store, { theme: theme });
}
},
toggleTheme: () => {
if (isPlatformBrowser(platformId)) {
const newTheme = store.theme() === 'dark' ? 'light' : 'dark';
ccConsumer.setThemeAttribute(newTheme);
localStorage.setItem('theme', newTheme);
patchState(store, { theme: newTheme });
}
},
}),
),
);

function getSystemTheme(): Theme {
return window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light';
// Hardcoded to 'dark' for now, as per decision.
return 'dark';
}

/* todo: create consumer interface and decouple AppThemeStore from CCAppThemeConsumer*/
@Injectable({ providedIn: 'root' })
export class CCAppThemeConsumer {
setThemeClass(theme: Theme): void {
const htmlElement = document.documentElement;
switch (theme) {
case 'dark':
htmlElement.classList.add('cc--darkmode');
break;
case 'light':
htmlElement.classList.remove('cc--darkmode');
break;
setThemeAttribute(theme: Theme): void {
document.documentElement.setAttribute('data-theme', theme);

const classList = document.documentElement.classList;

if (theme === 'dark') {
classList.add('cc--darkmode');
} else {
classList.remove('cc--darkmode');
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
/>

<div class="flex items-center gap-1">
<fast-svg name="clock" size="16" />
<fast-svg name="clock" aria-hidden="true" size="16" />
{{ articleDetails().readingTime }} min
</div>
</div>
Expand Down Expand Up @@ -53,6 +53,7 @@ <h1 id="article-title" class="flex text-[40px] font-bold">
</section>
<aside class="order-3 col-span-12 lg:col-span-4">
<al-author-card
[articleCard]="true"
[author]="articleDetails().author"
[clampText]="true"
[linkable]="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,15 @@ type ShareItem = {
[href]="item.href"
target="_blank"
>
<fast-svg class="text-al-foreground" [name]="item.icon" size="28" />
<fast-svg
aria-hidden="true"
class="text-al-foreground"
[name]="item.icon"
size="28"
/>
</a>
}
<a
role="button"
<button
[attr.aria-label]="t('articleShareIcons.urlAriaLabel')"
[class.url-icon-animated]="animating()"
[cdkCopyToClipboard]="articleUrl()"
Expand All @@ -43,17 +47,19 @@ type ShareItem = {
>
<fast-svg
name="link"
aria-hidden="true"
class="text-al-foreground"
[class.!hidden]="animating()"
size="28"
/>
<fast-svg
name="circle-check"
aria-hidden="true"
class="text-al-foreground"
[class.!hidden]="!animating()"
size="28"
/>
</a>
</button>
</div>
`,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
[attr.strict]="config.strict ? '1' : '0'"
[attr.emitmetadata]="config.emitMetadata ? '1' : '0'"
[attr.inputposition]="config.inputPosition"
[attr.theme]="config.theme"
[attr.theme]="theme()"
src="https://giscus.app/client.js"
crossorigin="anonymous"
async
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import {
GISCUS_CONFIG,
provideComments,
} from '@angular-love/blog/articles/data-access';
import { AppThemeStore } from '@angular-love/data-access-app-theme';

@Component({
selector: 'al-giscus-comments',
imports: [],
templateUrl: './giscus-comments.component.html',
styleUrl: './giscus-comments.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
Expand All @@ -30,6 +30,7 @@ import {
export class GiscusCommentsComponent {
readonly config = inject(GISCUS_CONFIG);
readonly translocoService = inject(TranslocoService);
readonly theme = inject(AppThemeStore).theme;

readonly lang = toSignal(this.translocoService.langChanges$, {
initialValue: this.translocoService.getActiveLang(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
<ng-container *transloco="let t; read: 'homePage'">
<section class="flex justify-center gap-3">
<div
role="tablist"
class="flex justify-center gap-3"
[attr.aria-label]="t('articleTypes')"
>
@for (category of categories(); track $index) {
<button
alPill
alFocusableItem
[tabindex]="selected().name === category.name ? 0 : -1"
role="tab"
[attr.aria-selected]="selected().name === category.name"
[variant]="category.name === selected().name ? 'flat' : 'outline'"
(click)="selected.set(category)"
(keydown)="onPillKeydown($event)"
>
{{ t(category.translationPath) }}
</button>
}
</section>
</div>

<section
class="mb-6 mt-6 grid grid-cols-1 gap-x-4 gap-y-4 md:grid-cols-2 md:gap-x-8 lg:grid-cols-3 lg:gap-y-8"
Expand All @@ -24,21 +33,28 @@
[imagePriority]="i < 2 ? 1 : null"
cardType="regular"
/>
@if (i === 1) {
<ng-container *ngTemplateOutlet="newsletterCard" />
}
}
} @else {
<al-article-regular-card-skeleton *alRepeat="take" />
<ng-container *ngTemplateOutlet="newsletterCard" />
}
<al-card
alGradientCard
class="md:max-lg:col-span-2 lg:col-start-3 lg:row-start-1"
>
<al-newsletter alCardContent />
</al-card>
</section>

<div class="flex justify-center gap-3">
<button al-button role="link" [routerLink]="selected().link">
<button al-button role="link" [routerLink]="selected().link" size="medium">
{{ t('categories.showAll', { category: t(selected().translationPath) }) }}
</button>
</div>

<ng-template #newsletterCard>
<al-card
alGradientCard
class="md:max-lg:col-span-2 lg:col-start-3 lg:row-start-1"
>
<al-newsletter alCardContent />
</al-card>
</ng-template>
</ng-container>
Loading