9dy_r{4zrbjJ+PFKTrMg0gyhLG*7C{`+fcPlX8bLYF1L+|
zl_#9`Yk{KlHo^ysm!jMb>*)(WPWJvk)nb8#SXz^4DZCG39BL7*Zzp;0Sf)9HIIC?X
zJ>;y$96k~4nPEGn!%)7Q$qA1!D&CAoQKb;aey=lbiiAVaJC3qsJN!IrdpH
zb`bPBia%N--)rUIvAteZJ*swMznwJ6W-{UXx{Pm{K40(iobEp*{4@o3>;RLc3A-;r
z;^(n9f6_^5If5*=0Bi$V+R97`NbA<|(Hb<|-STon@h;xREJsPAa)|Pf&B+m;9~cXP
z&h_)Eb}_2*eI8^40kIwmem&_wyw0-eycFbG($}q4_a{`W10E@txOkb4_Qf-`#To)0
z$)A$@-~&O-f4HY_Up(B~(jz|A;NUo|w9y&0f;b}byDh*{W(~He8jfv6%h$Z9%Ws8a
z)(H9uG=*@co+bOeey^!eZt3aHzTBsN&{d`vV8xsk|4fc>0dUL<23MYsG4%WulFgs4pY~1@i59MWO9N`JIA?S^zW)AVZ}bU6P0>Jq
zBMsGRvoNIATx91uhcNl+-&O7}U)#~WU1S`|f^fS2*V=8*-GkQ|E1Qmw^{LO4+fSI`
zlPFApj}p4yojYA~HJ|K+Qj%dmN-gJS#8Erv$4uoWOyI;Ge
zAEJs|^EBP?tyOcib$@_Fvy+VXpS--rD&fz-wy0>l>XO>lMT
zskV|g=zJCEI0}CJLdIl{*q%4@*~LMV3hg_ai2-xF#pe}lbjs^-APf^QJT&=_7I78k
zY0F&j@31hUn3bfOGQ32x_7H2z%BN$8oh|gWTuhOH+1B}N%(9Yqo{TwKJ#Jhm@awUM
zu@~&;+sr+gi(-r`pPb&?t;G!D0h0Ji>G(9=uBJcXgdrlAa&WfagsQsEcKC*wn*aea
zS8PEk2$q_r@RQyXGF^1Ry(QV)$+LZE-_L`1E^lC2<~$~=oAB|9J%N)vU8}$xvjl`P^vfZjB^9LL56&ouu
zP};&vranfEZY9hX?Vsvzkc(k~1zzfFF#TrWt_v~4ZtiLY_dm%U3|Ns^3XJqz}<>BXs^@jL}`silY_HEQNE{MSd^Uk^mVHmFU_73
zyK#cVBI2Y#@s?z1x((7`;yH*}{6M)w-a3)BN0q8GJhHn)zJ3sSyoe7;ykiSeTogyWjWu?!aq-
zLFnK#Bvz3B{c5$XfsWmFlksW%h>W-QD*bY`akH^CH6U^!@hBo^A*Y|dlwj2Eq5Y!h
zuM!^nc{4sDEbMIVwd*PCdzMm3ck3Ug`+lm^h^=0#c>O26inKxcqfnhIt{cmo;z=WF
zw}2_~LARlMeAiJz8^J9XEJ&F%wQ6DMq<7}tH9!hV91{_cA7Pc
z8Um8Pu<F(7PDmVE!s^QlC<_%c~Y5c(;b4BN4Fa2y`VrDKz*#)fEw8PhV=<(39`xiXqzeG3>_%LE~x{hsa1M8M=xWcHOcr((Y>(eMX1d!Fle(SpSkF*5mK2@
z^=~=Bjmf=S_rXjRsQ1{u^#D1b
z=m(b-#^RJ5(PUYVaxKRnAP7Vq`e{s$-QhXn8bS`&D7+nR!%{pp--TY-WHw)gYFvs%
z>uGc#u-0aJrxC)QIqR%um%n!zd(#Sw+_g5%s1{lZhLceAYxkFLTGI$r^v<~Z&dR&1
zdbNWX-6UHG!;m~}-^E^z%jgGWkIY5$a#U!-6|F1V7qWh(Th=VS?!&vvS#I439@O
z{yGc)@q=y7#hba9c40ctoz4dHr`Xx-`)dtj^UyL*$DA|5Vma$_=
zwGa0!|IKc}@E!2gOpa)#?l#$U>HGl=XjWe>Bo|7{-7#xwruVcMECI5P)>E36V&qu0
zN|keTmC*n8ol>9zJ-t%!D{PZl+8yI^`cceX!Q3ihVBkj11n}KpM-{7%AgzI<4qc_p
ze{lq*2s=gK#cz4(%BQ~ufz|_0c0A5Zd3McPI0|aWLMEbCk7yezrHJXJn(3tH1c7_g
zE@|voLf=ZzNkO?ps9H`(4s@9;e?CW#TM4YEwU5LI)eF
z-sf1Xk0QfXRzdGJH$8~sJjR#d%einH5>wmt6Xsl8S1iMm49tb(eiAOQuVrSTtK*o0
z+e_;tu0sm>y9!XPg*Q3*%w1?0o|>Iiah{G%`a?^u#ce5H@8Po5I3~|Rai1o9o*}M;N!;BICUZS*`hP;rVIWqWDvpQZzv#!
zJty)0!(b(usL>L}_iXWEP;6THoup%EDS{NbF6B6Qz*F(WadSh-q0aIs?fHFROXsQ(
zK9K8;$sOJ7)zMOTbO$!2s&IQav>^m`mElW{Z4Xpd`d=7KUTdLu<+^Zt*&qx^OIzTMI8z>ur3#U>^beZvEOxH
zg=*Ze>p;AHCYnpv=lK<phXr6R2z1tbSYj|#9EWIVad?y49h=eIMVq9{H6Sdt
zM5YuaVX-0nu9pA3(FPJwH$fGUY&~&wzb6nIC3~>ZoJG5O?&G^4Hj3N+v>`>87Hc)S
z8J%;wR)2ThDqVOW`^5y+Mf-2Dyz~MafMCtpU)B{s39AG7Cl=$3>jMH#Xk
z8q|d4`l|io`MKg0g0=@4NfZV?(rmqstBTB}of0BHzvA&5&V0`GMys=l^Yzc`DAu8&+G4o-Z8CRV4yQ(Z_XC8P^S{8L4HtLLWO7LGA|tPmAju8M%HQjqB(AXZArLt1NpHJ1;
zb?-ukL{S~fphSnsPYh-?X=-TQ$$%#>z+dWKv!CVySYrL|f9=Ia3#(dIP>gg(;5N=+
zI|YBJZ6Ip~(eBi`iZi$c_Xpn^JF0I#$&z`-2xo)3Us>fs;W(Fh?d;qrMLN>HwoqCM
z8*0GJ9C+DkZ78POgSWIiTlFttrj!Q?GEJ`{`LZMUhPdGY$lL8wxVXR_
z_afSZM5Gg-zb(rX*Ex7-Zv?dwYSQ~y!Z+lvh^SWzbs_gh7<412EV_<$
zjKd$uB6sB4XeuPL@ZsT6@F_7?;iE3ImPd#7gf(cKUHd5W!%A#BF=gx%S5xR|RmYKqNpJqllaa{?s38p=`C{Y?Ia_d#t}fo$!Nh
zNi{f0`y>y-kvW`;22~XZ=R!0kax^NV&Tn=C>mki!)A{bbl#Z>eGRr1Y@uS6K2;31-
zy~Ih$-F?~J#K7=|Y8Sin1^>dD}1kwA~xdncj1~VJUg+j#A?igVeYz)KYcdT*``e}bSqn!
z)j*ZDNd$~So@$S-3tz^+I<78vwhRI_n;1EO=Er|W>#jxgc+dSb5FG8g<5TlY(xADS
z>ouMj?yP?tE@Ue0VCi+ZTP+3qtRNHCy0NKeLW%ZOGO{w=1O?ikQ438#mD6EIiWa03
z3~S;}Rs-sucmYklz*k_{!Wv5%lnu&mEvnDJPVf!1HzFujhswLgEpYDUw3Ooq(RuKb
znI9{R75vg1Ow@p$qQ%wyp;Aua`^h~4_i+S^Zu<*LEvnv>$gW!`+IBQsotHfY+!$^|
zZgEXY0J)3n*gx<}b>TrKG)yhJFC!u#sJFlkH$2}Ra}C7lbt`B3Seq!t@XxeAIEH;D
z2Wf3=R^>(EB;HM9F%QlRJ5
zr~ly53#wghudRArwN1VROE!^{V1>SMIlXw5DzBcU-)ygbw<*V)KXjzHe)8>(6T9pz
z&-^)rD~vS=0=m=$bP$qpcTHb@uO!koRAeX~8G~c1TgQ=H#U2SQhRjzS5TIy{CVrSvYr1KP6Y>}Usw~!BV{HXxc+2k+{0Kp{Tnvxp<
zd;}QjQHnru>liC5OZwy!v@w&z2}MfugENb_d;kcW<&fNutS8)O1xGJ
z?Mk&y#bx7XkS45e0Oq=@_yDRev~?*kC5@U!_ill&$pXK!IyDYnIBzkG1g4hD$NSFi
z09)JPgObxd{S_j!Y2Htzz*5d-9JARWLx2_^Cp3Fzi$F5p^fmMJ=I|iz83TtluNx~TM4>KfQtsD!J4A2o2NFRejedQKu}61$PO
z*=xL{63K*}GKG5sl`PBZK09Dz*T-=O&E{M4Tg(|D6cr9`Nni1fQ1dX)$5R6T2DTz_
zmgjl-MTk~C7N9mV0P%jZt;IPr#JCEITJ8Sboi0gC%_;61jr2v~)Q-U118U*GjuDu*
zJi$WGS2UD~xW?>`O}PTbt7#~3VksS~dA!r-;-~4HF8)YJ&F*-KXr1DxOcjX`VzeW;
zu`E{@1I{mYBjwq_Br_SyJN_>t`@M@tdM2@MpOLQz6JCz@av!gx=w-uh31_xBw`RZ1
zCxJ!j&8TZv8XMzWW`m6@N>w1T3|-y40+hiUXe*h#;~KOnl`lic^wD
z*gM>o%vwQSEu{_!J(33)4O@AvhlC?p5P$%DTv*ai!leXVX2O
z(2^hW;}#H4uoAooMaxzF3D5~23F;_I8%uR!)Rq7U@V-^dFosel9K}i*t#>r6Tr;8d
zmH8*r!0tTvt$3A`!kt;KkjwofA&FP*M*IS^Zg{)e7N;{f->QvHK;YW|f1&el>BIzn
z50~@GkOAMsry0>ZbGB+fS|+y@RE|x0sTnowI(T|FB-IWu{(KTF*h`an(IR||?(qVQ
zYRj)jOFDjIa7?bORr&)eS{FyK{E%oCjLppH!A}c)i0RCR1Ggw!AGA0o*~lB)(!Mpa
z5i&>a9gKvQG=-GJ8qEa-rhVx{Z
zemVKjVkN&i<#n-Ra7(r7^+Zkk$5xbv)jPxHiEc7PavEVJ$@2LlIF2?S-kV(Ia_KPt
zB^?h+RQV)dgnv29UoiyRu25pYO@8agrA~W^@@c%U+7t+!*d!sr!qBL|Jd0g;GOZGt+?&T)
zHeGD37Y=a2$0BDiN~k2aUKPvr4upb@o67AneU>7VYg?Cwe#5(1_|CPU^wmhx;wtuL
zHVa%%&kax-Fl-bZ_69FWSsuoV36C#G#BBqkZK@;1oQb=XO%)rd9yATgk^nBMI}uLj
z78CfX90VrFqYp=!?cZGggsbr{6q(BAQhixLJrxfRC$A08nf?=j(8k!6_xh~+v7q2N
zX?us$RVl4i&A}2LB9#JHm`}kbPag
zz%Ny#h$Zh@b;}tBga6_65?RNh@r*0pj|4}6?*3w0k2Wx3V*TG;TMo!S_0M*8`FG{N
zRQ0nCG8uQ~v<~G+bpRsye@cB8%cbg-UD0H(mqlsjB{>fjz-q@VzvX&5Zr3~3g#I_3
z8HqC%oYX)@|bOE{ImLx@cP#|UC*q~Eg-_P49O*>5@Ft}{64_&@A(b@{pBZI6rfs&(E#RFy)Fwb-SVVtNZfiZ4{eA``I`t;0B7&FvP2*xu7+uMGlh
zOyKX$GJXX@WbQfShVzDtc-S)P^4B-FjCe{=msmeYe(HTZv^WTV<1HZ>I0%u_i6Fq*
zjjnsmW|bbkT<5sHc*We6mTsu2)Jzya4r4iFw+pgMmCX3RBfH78)t(J9=*>aad_LaxjdRavQC~x$Dpgk-z+FalHHE)F
z=?PSD4dyU0BAvy%$%kvXK8n8CCQ2uKNUpMT{jo#Klum6IH}JKTFyI-LuR!xyx!fe@
z9e)jFblCX6HSb`(!kZ@h1Cz|lCd?g{G&K@kA_tI0=&QLIBFwn?_bn4k%ZftjJE(QK4Qzm6p@f&VXgOu?QSxIm%6
zPL8RnrJVVY$Rdx7d5VN4Bk}{GjEg**X4ZZCRIS{)zN0B-FVO+0O`l#|{VjJH
z6&R`J=Q8I2)~r*j#E^7|Ik8@gjBHgx;~vpd=~CA}RO)*#Po~Sy>jQSm?1`NN1%UGp
zw4&q9t9&mw+p}yIkJ3kXnfC7vhQbFueKx9kHC>D{<#)yw)TxIdh$3ax{-7@wkO4_B
zkRnk>a0wdg*!S;=Jk7m$4oi^!%}UBWK*+&hFDmX&ACO)g&vExTAv{pK+JoOxSV)@PX`Z#Q8^MjQs;XJ!^1}m%P}u~>
z^9geCtptb{c2kB>J}3AmCjUnizf3M#N5Wx40X!-t{$8L@81ao#<4VKuu6_nFGmHWS
zJBeKjQp~IBBO^TCOcLu4>B?%&jq1kZwV-~pH*`6w9BWva$*-+b66G(E~4h+QpfaQ(e%&y*fPmhm*=!#H-t!^iaU;$^nGPc{c0+mP4THfj!%l
zVQ+ePV${J+&B%f~rzyPU{K5oumIj}rYf0zy4Cw>SBc&ECLL+U5oLo#)@fTWu4RBF^
zu@XlLOC(Kn&WR0D{MEB$cZ>qPJtDgHHe1WoS4lgGd@kv~V-8Guymi*^lwmX`4*nG(
zjoWabzse|R;CpJT&ugJImt1w25{R71)avXKRrT8lbr;TJq~+NPlaY+|R}KSy-XH9s
zp@gLpPOr(qfX~X{C{?r7N70wjpy1_HY^(%o<^!T7#Rw3hw7yi0zW&R&Aw4N15)^}(
z&x^_|8coNj@89E~$H`0i+3OgQFd0kvryHj$5It?&jdT$CtI$(p2bnj_4z39?lQ#>D97!QDmqhoSUb!-vHx-LZ#g{R{h2=-ym?0agvucwaeZUq
zL$9W)<;V2qqukT<^fV0qv?qdg6|L};VSmE1^P0_!xG4tK9WKaVxj+2;WZoeWuyuT=-f5DMoG*q
zQFu3(T9runw-Y1wE=HNYBPXt>_6IN0(Okf-JZoTWX|wd2l?uV+=H$2b_M8C
ze_kqDO-;!9bin?_)d-aDW?PMJBCg%k3nfZW5<0PkFojyZdh9B7&5avC8GjFgp=&wn
zk>xAFqcY8t0ITWc5r;rCYDTgVeLI-y2L|cMH9IE8Q2t+@t4pWavvVPv*C;Jcy@&;kLhb-%sYF%==eX^v7V|lVA!7z+zM(2Y({eq
z^WZ}i*HZ<2Yo!Bfih%f!xs6*p3f_I=8Pw$7eO_XW2Sox=8zFYKIuFtL1yqWR2~kzg
zTa^jRWvgr-2Zdc+GlR}8Y5&v{gkX}-yBAqcSx<_ZoRgquDM6>2nlE&RJD0L+=-xY_
zBLBqB8`A_mtTR=Fmz~@*AbD1814h@)mLq?pPZoA-$~1l9c39(Y*b*52@0ScfN?cy7
JQbaG{{{U#)s)PUl
diff --git a/src/features/common/assets/jwt-logo.component.tsx b/src/features/common/assets/jwt-logo.component.tsx
new file mode 100644
index 00000000..57db4704
--- /dev/null
+++ b/src/features/common/assets/jwt-logo.component.tsx
@@ -0,0 +1,82 @@
+export const JwtLogoComponent: React.FC = () => {
+ return (
+
+ );
+};
\ No newline at end of file
diff --git a/src/features/common/assets/jwt-wordmark.component.tsx b/src/features/common/assets/jwt-wordmark.component.tsx
new file mode 100644
index 00000000..7cb4a514
--- /dev/null
+++ b/src/features/common/assets/jwt-wordmark.component.tsx
@@ -0,0 +1,30 @@
+export const JwtWordmarkComponent: React.FC = () => {
+ return (
+
+ );
+};
\ No newline at end of file
diff --git a/src/features/common/components/errors/error-page/error-page.component.tsx b/src/features/common/components/errors/error-page/error-page.component.tsx
index 35d32065..96f0f8c5 100644
--- a/src/features/common/components/errors/error-page/error-page.component.tsx
+++ b/src/features/common/components/errors/error-page/error-page.component.tsx
@@ -1,29 +1,24 @@
import React from "react";
import styles from "./error-page.module.scss";
import { ShellComponent } from "@/features/common/components/shell/shell.component";
-import { RibbonComponent } from "@/features/common/components/bars/ribbon/ribbon.component";
import { MobileHeaderComponent } from "@/features/common/components/headers/mobile-header/mobile-header.component";
import { HeaderComponent } from "@/features/common/components/headers/header/header.component";
import { FooterComponent } from "@/features/common/components/footer/footer.component";
-import { LayoutDictionaryModel } from "@/features/localization/models/layout-dictionary.model";
import { ThemeCookieValues } from "@/features/common/values/theme.values";
-import { getImageDictionary } from "@/features/localization/services/images-dictionary.service";
-import { SiteLogoComponent } from "@/features/common/components/site-logo/site-logo.component";
+import { getLayoutDictionary } from "@/features/localization/services/language-dictionary.service";
interface ErrorPageComponentProps {
languageCode: string;
themeCode: ThemeCookieValues;
- dictionary: LayoutDictionaryModel;
children: React.ReactNode;
}
export const ErrorPageComponent: React.FC = ({
languageCode,
themeCode,
- dictionary,
children,
}) => {
- const images = getImageDictionary(languageCode);
+ const layoutDictionary = getLayoutDictionary(languageCode);
return (
@@ -31,35 +26,19 @@ export const ErrorPageComponent: React.FC = ({
}
- ribbon={
-
- }
+ dictionary={layoutDictionary}
+ themeCode={themeCode}
/>
}
- ribbon={
-
- }
+ dictionary={layoutDictionary}
+ themeCode={themeCode}
/>
{children}
}
+ dictionary={layoutDictionary.footer}
/>
diff --git a/src/features/common/components/footer/footer.component.tsx b/src/features/common/components/footer/footer.component.tsx
index 42859a07..719ff3db 100644
--- a/src/features/common/components/footer/footer.component.tsx
+++ b/src/features/common/components/footer/footer.component.tsx
@@ -15,25 +15,23 @@ import { DEFAULT_LANGUAGE_CODE } from "@/features/localization/localization.conf
import { sitePaths } from "@/features/seo/site-tree";
import { createUrlPath } from "@/libs/utils/path.utils";
import { SiteBrandComponent } from "@/features/common/components/site-brand/site-brand.component";
-import { StaticImageMetadataModel } from "@/features/common/models/static-image-metadata.model";
import { Button } from "react-aria-components";
+import { Auth0LogoComponent } from "../../assets/auth0-logo.component";
+import { getImageDictionary } from "@/features/localization/services/images-dictionary.service";
interface FooterComponentProps {
languageCode: string;
dictionary: LayoutDictionaryModel["footer"];
- auth0Logo: StaticImageMetadataModel;
- siteLogo: React.ReactNode;
}
export const FooterComponent: React.FC = ({
languageCode,
dictionary,
- auth0Logo,
- siteLogo,
}) => {
const [modalState, setModalState] = useState(
ModalStateValues.CLOSED,
);
+ const images = getImageDictionary(languageCode);
const languagePathPrefix: string =
languageCode === DEFAULT_LANGUAGE_CODE
@@ -66,9 +64,7 @@ export const FooterComponent: React.FC = ({
contentClassName={styles.content}
>
-
- {siteLogo}
-
+
@@ -162,17 +158,7 @@ export const FooterComponent: React.FC = ({
target="_blank"
href="https://auth0.com/"
>
-
+
{dictionary.copyright}
diff --git a/src/features/common/components/footer/footer.module.scss b/src/features/common/components/footer/footer.module.scss
index 0f39c24d..44e04595 100644
--- a/src/features/common/components/footer/footer.module.scss
+++ b/src/features/common/components/footer/footer.module.scss
@@ -267,8 +267,5 @@
padding-bottom: 1rem;
row-gap: 2rem;
- color: $neutrals-light-100-snow;
-
- @media #{$breakpoint-dimension-xs} {
- }
+ color: var(--color_fg_default);
}
diff --git a/src/features/common/components/headers/header/header.component.tsx b/src/features/common/components/headers/header/header.component.tsx
index 733b84ca..7002824d 100644
--- a/src/features/common/components/headers/header/header.component.tsx
+++ b/src/features/common/components/headers/header/header.component.tsx
@@ -1,6 +1,6 @@
"use client";
-import React from "react";
+import React, { useCallback, useMemo, useState } from "react";
import { usePathname } from "next/navigation";
import { DEFAULT_LANGUAGE_CODE } from "@/features/localization/localization.config";
import { LayoutDictionaryModel } from "@/features/localization/models/layout-dictionary.model";
@@ -10,19 +10,32 @@ import styles from "./header.module.scss";
import { BoxComponent } from "@/features/common/components/box/box.component";
import Link from "next/link";
import { SiteBrandComponent } from "@/features/common/components/site-brand/site-brand.component";
+import { ThemePickerComponent } from "../../theme-picker/theme-picker.component";
+import {
+ getSanitizedThemePickerCodeValue,
+ isLightThemePreference,
+ isSystemThemePreference,
+} from "@/features/themes/services/theme.utils";
+import { SystemIconComponent } from "../../bars/ribbon/assets/system-icon.component";
+import { LightIconComponent } from "../../bars/ribbon/assets/light-icon.component";
+import { DarkIconComponent } from "../../bars/ribbon/assets/dark-icon.component";
+import {
+ ThemeCookieValues,
+ ThemePickerCodeValues,
+} from "@/features/common/values/theme.values";
+import { ThemeModel } from "@/features/common/models/theme.model";
+import { savePreferredThemeInCookie } from "@/features/themes/services/theme.client.utils";
interface HeaderComponentProps {
+ themeCode: ThemeCookieValues;
languageCode: string;
- dictionary: LayoutDictionaryModel["header"];
- siteLogo: React.ReactNode;
- ribbon: React.ReactNode;
+ dictionary: LayoutDictionaryModel;
}
export const HeaderComponent: React.FC = ({
+ themeCode,
languageCode,
dictionary,
- siteLogo,
- ribbon,
}) => {
const pathname = usePathname();
const pathnameSegments = getPathnameSegments(pathname);
@@ -37,22 +50,68 @@ export const HeaderComponent: React.FC = ({
? sitePaths.root
: createUrlPath([languageCode]);
+ const themeOptions = useMemo(
+ () =>
+ dictionary.ribbon.themePicker.options.map((option) => {
+ return {
+ code: option.code,
+ label: option.label,
+ icon: isSystemThemePreference(option.code) ? (
+
+ ) : isLightThemePreference(option.code) ? (
+
+ ) : (
+
+ ),
+ };
+ }),
+ [dictionary.ribbon.themePicker.options]
+ );
+
+ const sanitizedThemePickerCodeValue = useMemo(() => {
+ return getSanitizedThemePickerCodeValue(themeCode);
+ }, [themeCode]);
+
+ const [currentTheme, setCurrentTheme] = useState(
+ dictionary.ribbon.themePicker.options.filter((element) =>
+ isSystemThemePreference(themeCode)
+ ? isSystemThemePreference(element.code)
+ : element.code === sanitizedThemePickerCodeValue
+ )[0]
+ );
+
+ const handleThemeSelection = useCallback(
+ async (value: ThemePickerCodeValues) => {
+ const themePreference = await savePreferredThemeInCookie(
+ value,
+ languageCode
+ );
+
+ if (themePreference) {
+ setCurrentTheme(themePreference);
+ }
+ },
+ [languageCode]
+ );
+
return (
-
- {ribbon}
-
-
- {siteLogo}
-
+
+
+
+
+
- {dictionary.links.map((link) => {
+ {dictionary.header.links.map((link) => {
const linkPath =
languageCode === DEFAULT_LANGUAGE_CODE || link.isExternal
? link.path
@@ -77,7 +136,14 @@ export const HeaderComponent: React.FC = ({
})}
-
-
+
+
+
+
+
);
};
diff --git a/src/features/common/components/headers/header/header.module.scss b/src/features/common/components/headers/header/header.module.scss
index f6a79992..3f27108d 100644
--- a/src/features/common/components/headers/header/header.module.scss
+++ b/src/features/common/components/headers/header/header.module.scss
@@ -1,22 +1,22 @@
@use "@/libs/theme/styles/variables" as *;
@use "@/libs/theme/styles/mixins" as *;
-.header {
- position: fixed;
- top: 0;
- right: 0;
- left: 0;
-
- z-index: 9001;
- backdrop-filter: blur(2rem);
-}
-
.container {
@include Container;
+ max-width: calc(100% - 2rem);
display: none;
-
+ position: fixed;
+ top: 1rem;
+ right: 0;
+ left: 0;
+ border-radius: 1.25rem;
+ margin: 0 auto;
+ box-sizing: border-box;
background: var(--color_bg_app_bar);
- border-bottom: 1px solid rgba(#555, 0.32);
+ border: 1px solid var(--color_bg_app_bar);
+ box-shadow: 0 12px 24px -12px rgba(0, 0, 0, .04);
+ backdrop-filter: blur(3rem);
+ z-index: 100;
@media #{$breakpoint-dimension-sm} {
display: block;
@@ -29,20 +29,37 @@
}
.content {
- @include InnerContentFlex;
+ display: flex;
+ width: 100%;
+ margin: 0 auto;
height: 100%;
position: relative;
- grid-column: 1 / -1;
align-items: center;
justify-content: space-between;
}
+.brand {
+ display: flex;
+ align-items: center;
+ height: 1rem;
+ margin-left: 1.5rem;
+ z-index: 100;
+}
+
+.navContainer {
+ display: flex;
+ justify-content: center;
+ width: 100%;
+ position: absolute;
+ left: 0;
+}
+
.navTabs {
- flex: 1;
display: flex;
align-items: center;
- justify-content: flex-end;
- gap: 3rem;
+ justify-content: center;
+ border-radius: 9999px;
+ padding: .25rem;
}
.navList {
@@ -50,39 +67,33 @@
padding: 0;
list-style-type: none;
margin: 0;
-
height: 100%;
-
- @media #{$breakpoint-dimension-sm} {
- gap: 2rem;
- }
-
- @media #{$breakpoint-dimension-md} {
- gap: 3rem;
- }
+ gap: 0.25rem;
}
.navList__item {
position: relative;
- border-bottom: 0.125rem solid transparent;
- color: var(--color_fg_link);
&[data-active="true"] {
- color: var(--color_fg_selected);
- border-bottom: 1px solid var(--color_border_selected);
+ a {
+ background-color: var(--color_bg_app_bar);
+ color: var(--color_fg_bold);
+ cursor: default;
+ }
}
}
-.navList__item,
.navList__item > a {
display: flex;
align-items: center;
-
- font-size: 0.875rem;
+ padding: .5rem 1rem;
+ color: var(--color_fg_default);
+ font-size: .875rem;
font-style: normal;
- font-weight: 500;
- line-height: 1.5rem;
- letter-spacing: 0.2px;
+ font-weight: 600;
+ line-height: 1.5;
+ letter-spacing: -.1px;
+ border-radius: 9999px;
&:focus-visible {
outline: solid 1px var(--color_border_focus);
@@ -90,3 +101,13 @@
border-radius: 0.125rem;
}
}
+
+.actions {
+ display: flex;
+ gap: .5rem;
+
+ @media #{$breakpoint-dimension-md} {
+ gap: 1rem;
+ margin-right: 1.5rem;
+ }
+}
\ No newline at end of file
diff --git a/src/features/common/components/headers/mobile-header/mobile-header.component.tsx b/src/features/common/components/headers/mobile-header/mobile-header.component.tsx
index ce7c1789..32f1080b 100644
--- a/src/features/common/components/headers/mobile-header/mobile-header.component.tsx
+++ b/src/features/common/components/headers/mobile-header/mobile-header.component.tsx
@@ -11,19 +11,17 @@ import { DEFAULT_LANGUAGE_CODE } from "@/features/localization/localization.conf
import { sitePaths } from "@/features/seo/site-tree";
import { createUrlPath, getPathnameSegments } from "@/libs/utils/path.utils";
import { SiteBrandComponent } from "@/features/common/components/site-brand/site-brand.component";
+import { ThemeCookieValues } from "@/features/common/values/theme.values";
interface MobileHeaderComponentProps {
+ themeCode: ThemeCookieValues;
languageCode: string;
- dictionary: LayoutDictionaryModel["header"];
- siteLogo: React.ReactNode;
- ribbon: React.ReactNode;
+ dictionary: LayoutDictionaryModel;
}
export const MobileHeaderComponent: React.FC = ({
languageCode,
dictionary,
- siteLogo,
- ribbon,
}) => {
const pathname = usePathname();
const [currentPathname, setCurrentPathname] = useState(null);
@@ -80,8 +78,6 @@ export const MobileHeaderComponent: React.FC = ({
return (
<>
-
- {ribbon}
= ({
contentClassName={styles.content}
>
-
- {siteLogo}
-
+
-
= ({
contentClassName={styles.menuContent}
>
- {dictionary.links.map((link) => {
+ {dictionary.header.links.map((link) => {
const linkPath =
languageCode === DEFAULT_LANGUAGE_CODE || link.isExternal
? link.path
diff --git a/src/features/common/components/headers/mobile-header/mobile-header.module.scss b/src/features/common/components/headers/mobile-header/mobile-header.module.scss
index f3f7e107..c5559533 100644
--- a/src/features/common/components/headers/mobile-header/mobile-header.module.scss
+++ b/src/features/common/components/headers/mobile-header/mobile-header.module.scss
@@ -1,22 +1,19 @@
@use "@/libs/theme/styles/variables" as *;
@use "@/libs/theme/styles/mixins" as *;
-.header {
+.container {
+ width: calc(100% - 2rem);
position: fixed;
- top: 0;
+ top: 1rem;
right: 0;
left: 0;
-
- z-index: 9001;
- backdrop-filter: blur(2rem);
-}
-
-.container {
- @include Container;
-
+ border-radius: 1rem;
+ margin: 0 auto;
background: var(--color_bg_app_bar);
- border-bottom: 1px solid rgba(#555, 0.32);
-
+ border: 1px solid var(--color_bg_app_bar_border);
+ backdrop-filter: blur(2rem);
+ z-index: 100;
+
@media #{$breakpoint-dimension-sm} {
display: none;
}
@@ -111,12 +108,8 @@
}
.menu {
- position: fixed;
- top: $main-nav-height-mobile;
- z-index: 99999;
- height: calc(100% - $main-nav-height-mobile);
- width: 100%;
- overflow: hidden;
+ opacity: 1;
+ transition: all .3s ease-in-out;
&[aria-hidden="false"] {
display: block;
@@ -134,16 +127,22 @@
}
.menuContainer {
- height: 100%;
- width: 100%;
- overflow-y: scroll;
- background: var(--color_bg_page);
+ width: calc(100% - 2rem);
+ position: fixed;
+ left: 1rem;
+ top: 4.5rem;
+ border-radius: 1rem;
+ background: rgba(0, 0, 0, .04);
+ border: 1px solid rgba(0, 0, 0, .04);
+ box-shadow: 0 1px 1px -.5px rgba(0, 0, 0, .04), 0 2px 2px -1px rgba(0, 0, 0, .04), 0 4px 4px -2px rgba(0, 0, 0, .04), 0 8px 8px -4px rgba(0, 0, 0, .04), 0 12px 12px -6px rgba(0, 0, 0, .04);
+ backdrop-filter: blur(2rem);
+ z-index: 100;
}
.menuContent {
@include InnerContentFlex;
- padding: 0 1.5rem 1.5rem;
width: 100%;
+ padding: .25rem;
}
.menu__list {
@@ -156,25 +155,27 @@
position: relative;
list-style: none;
width: 100%;
- border-bottom: 1px solid $neutrals-functional-300;
margin: 0;
- padding: 1.5rem 0.5rem;
}
.menu__item__link {
- font-size: 1.25rem;
- line-height: 1.75rem;
- letter-spacing: -0.1px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: .75rem;
+ width: 100%;
+ font-size: 1rem;
+ letter-spacing: -.1px;
color: var(--color_fg_bold);
text-decoration: none;
font-weight: 500;
- transition: color 0.2s;
+ transition: color .2s;
cursor: pointer;
user-select: none;
- padding-bottom: 0.25rem;
- border-bottom: 1px solid transparent;
+ padding: .75rem .5rem;
&[data-active="true"] {
- border-bottom: 1px solid var(--color_fg_bold);
+ background-color: hsla(0, 0%, 100%, .5);
+ box-shadow: 0 0 1px 2px rgba(0, 0, 0, .04);
}
}
diff --git a/src/features/common/components/layout/page-header/page-header.component.tsx b/src/features/common/components/layout/page-header/page-header.component.tsx
index 468f4626..6ba75f9c 100644
--- a/src/features/common/components/layout/page-header/page-header.component.tsx
+++ b/src/features/common/components/layout/page-header/page-header.component.tsx
@@ -1,5 +1,6 @@
+"use client";
+
import React from "react";
-import { RibbonComponent } from "@/features/common/components/bars/ribbon/ribbon.component";
import { MobileHeaderComponent } from "@/features/common/components/headers/mobile-header/mobile-header.component";
import { HeaderComponent } from "@/features/common/components/headers/header/header.component";
import { ThemeCookieValues } from "@/features/common/values/theme.values";
@@ -8,42 +9,26 @@ import { getLayoutDictionary } from "@/features/localization/services/language-d
interface PageHeaderComponentProps {
languageCode: string;
themeCode: ThemeCookieValues;
- siteLogo: React.ReactNode;
}
export const PageHeaderComponent: React.FC = ({
themeCode,
languageCode,
- siteLogo,
}) => {
const layoutDictionary = getLayoutDictionary(languageCode);
return (
- <>
+
- }
+ dictionary={layoutDictionary}
/>
- }
+ dictionary={layoutDictionary}
/>
- >
+
);
};
diff --git a/src/features/common/components/layout/page-layout/page-layout.component.tsx b/src/features/common/components/layout/page-layout/page-layout.component.tsx
index 5c1cdfdb..bc4d9d7b 100644
--- a/src/features/common/components/layout/page-layout/page-layout.component.tsx
+++ b/src/features/common/components/layout/page-layout/page-layout.component.tsx
@@ -4,9 +4,7 @@ import { getLayoutDictionary } from "@/features/localization/services/language-d
import { ShellComponent } from "@/features/common/components/shell/shell.component";
import { FooterComponent } from "@/features/common/components/footer/footer.component";
import { ThemeCookieValues } from "@/features/common/values/theme.values";
-import { getImageDictionary } from "@/features/localization/services/images-dictionary.service";
import { PageHeaderComponent } from "@/features/common/components/layout/page-header/page-header.component";
-import { SiteLogoComponent } from "@/features/common/components/site-logo/site-logo.component";
interface LayoutComponentProps extends PropsWithChildren {
languageCode: string;
@@ -19,7 +17,6 @@ export const PageLayoutComponent: React.FC = ({
children,
}) => {
const layoutDictionary = getLayoutDictionary(languageCode);
- const images = getImageDictionary(languageCode);
return (
@@ -74,14 +71,11 @@ export const PageLayoutComponent: React.FC = ({
}
/>
{children}
}
/>
diff --git a/src/features/common/components/site-brand/site-brand.component.tsx b/src/features/common/components/site-brand/site-brand.component.tsx
index 8ab48d77..f7e7fd1b 100644
--- a/src/features/common/components/site-brand/site-brand.component.tsx
+++ b/src/features/common/components/site-brand/site-brand.component.tsx
@@ -1,18 +1,34 @@
import React, { PropsWithChildren } from "react";
import styles from "./site-brand.module.scss";
import Link from "next/link";
+import { getImageDictionary } from "@/features/localization/services/images-dictionary.service";
+import { SecondaryFont } from "@/libs/theme/fonts";
+import clsx from "clsx";
+import { JwtLogoComponent } from "../../assets/jwt-logo.component";
+import { JwtWordmarkComponent } from "../../assets/jwt-wordmark.component";
interface SiteBrandComponentProps extends PropsWithChildren {
path: string;
+ languageCode: string;
}
export const SiteBrandComponent: React.FC = ({
path,
- children,
+ languageCode,
}) => {
+ const images = getImageDictionary(languageCode);
+
return (
-
- {children}
+
+
+
+
+
+
+
+
+ Debugger
+
);
};
diff --git a/src/features/common/components/site-brand/site-brand.module.scss b/src/features/common/components/site-brand/site-brand.module.scss
index 459c9a51..09dbd56c 100644
--- a/src/features/common/components/site-brand/site-brand.module.scss
+++ b/src/features/common/components/site-brand/site-brand.module.scss
@@ -1,8 +1,11 @@
.brand {
+ width: 100%;
display: flex;
align-items: center;
- height: 2rem;
+ height: 1.5rem;
+ gap: 0.5rem;
position: relative;
+ cursor: pointer;
svg {
height: inherit;
@@ -15,3 +18,57 @@
border-radius: 0.125rem;
}
}
+
+.container {
+ position: relative;
+ display: flex;
+ height: 100%;
+ align-items: center;
+}
+
+.brand__logo {
+ position: relative;
+ display: flex;
+ align-items: center;
+ height: 1.5rem;
+ animation: rotate 10s linear infinite;
+}
+
+@keyframes rotate {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(1turn);
+ }
+}
+
+.brand__wordmark {
+ position: relative;
+ display: flex;
+ align-items: center;
+ height: 1rem;
+}
+
+.brand__headline {
+ display: flex;
+ flex-direction: column;
+ color: var(--color_fg_bold);
+}
+
+.brand__title {
+ display: flex;
+ font-size: 1.25rem;
+ line-height: 1.25rem;
+ margin-top: 0;
+ letter-spacing: 0.02rem;
+}
+
+.brand__subtitle {
+ display: flex;
+ font-size: 1rem;
+ font-weight: 500;
+ line-height: .75rem;
+ margin-top: 1px;
+ letter-spacing: .02rem;
+}
diff --git a/src/features/common/components/site-logo/site-logo.component.tsx b/src/features/common/components/site-logo/site-logo.component.tsx
deleted file mode 100644
index 3e981687..00000000
--- a/src/features/common/components/site-logo/site-logo.component.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import React from "react";
-import styles from "./site-logo.module.scss";
-import Image from "next/image";
-import clsx from "clsx";
-import { SecondaryFont } from "@/libs/theme/fonts";
-import { getImageDictionary } from "@/features/localization/services/images-dictionary.service";
-
-interface SiteLogoComponentProps {
- languageCode: string;
-}
-
-export const SiteLogoComponent: React.FC = ({
- languageCode,
-}) => {
- const images = getImageDictionary(languageCode);
-
- return (
-
- );
-};
diff --git a/src/features/common/components/site-logo/site-logo.module.scss b/src/features/common/components/site-logo/site-logo.module.scss
deleted file mode 100644
index 76d0d92a..00000000
--- a/src/features/common/components/site-logo/site-logo.module.scss
+++ /dev/null
@@ -1,35 +0,0 @@
-.container {
- position: relative;
- display: flex;
- height: 100%;
- align-items: center;
-}
-
-.brand__logo {
- position: relative;
- display: flex;
- align-items: center;
-}
-
-.brand__headline {
- display: flex;
- flex-direction: column;
- margin-left: 0.625rem;
- color: var(--color_fg_bold);
-}
-
-.brand__title {
- display: flex;
- font-size: 1.25rem;
- line-height: 1.25rem;
- margin-top: 0;
- letter-spacing: 0.02rem;
-}
-
-.brand__subtitle {
- display: flex;
- font-size: 0.75rem;
- line-height: 0.75rem;
- margin-top: 1px;
- letter-spacing: 0.02rem;
-}
diff --git a/src/features/common/components/theme-picker/theme-picker.component.tsx b/src/features/common/components/theme-picker/theme-picker.component.tsx
new file mode 100644
index 00000000..2981e145
--- /dev/null
+++ b/src/features/common/components/theme-picker/theme-picker.component.tsx
@@ -0,0 +1,41 @@
+import styles from "./theme-picker.module.scss";
+import { ThemePickerCodeValues } from "../../values/theme.values";
+
+interface ThemePickerComponentProps {
+ options: {
+ code: ThemePickerCodeValues;
+ icon: React.ReactNode;
+ label: string;
+ }[];
+ selectedOptionCode: string;
+ handleSelection: (value: ThemePickerCodeValues) => Promise;
+}
+
+export const ThemePickerComponent: React.FC = ({
+ options,
+ selectedOptionCode,
+ handleSelection,
+}) => {
+ return (
+
+
+ {options.map((option) => {
+ return (
+
{
+ console.log(option.label)
+ await handleSelection(option.code);
+ }}
+ >
+ {option.icon}
+
+ );
+ })}
+
+
+ );
+};
diff --git a/src/features/common/components/theme-picker/theme-picker.module.scss b/src/features/common/components/theme-picker/theme-picker.module.scss
new file mode 100644
index 00000000..90049ed9
--- /dev/null
+++ b/src/features/common/components/theme-picker/theme-picker.module.scss
@@ -0,0 +1,50 @@
+.container {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.wrapper {
+ display: flex;
+ background-color: var(--color_bg_app_bar);
+ border: 1px solid var(--color_border_default);
+ border-radius: 9999px;
+ overflow: hidden;
+ width: -moz-fit-content;
+ width: fit-content;
+ padding: 0.25rem;
+ gap: 0.125rem;
+}
+
+.option {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--color_fg_default);
+ cursor: pointer;
+ padding: 0.25rem;
+ position: relative;
+ transition: all 0.2s ease;
+ border-radius: 9999px;
+ width: 2.5rem;
+ height: 1.75rem;
+
+ &[data-active="true"] {
+ background-color: var(--color_bg_layer);
+ color: var(--color_fg_bold);
+ box-shadow: 0 1px 2px rgba(0, 0, 0, .1);
+ }
+}
+
+.option__icon {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+ width: 100%;
+
+ svg {
+ height: 1rem;
+ width: 1rem;
+ }
+}
diff --git a/src/features/common/models/static-image-metadata.model.ts b/src/features/common/models/static-image-metadata.model.ts
index f65aabdb..c9e1aaa7 100644
--- a/src/features/common/models/static-image-metadata.model.ts
+++ b/src/features/common/models/static-image-metadata.model.ts
@@ -1,6 +1,6 @@
export interface StaticImageMetadataModel {
src: string;
alt: string;
- width: number;
- height: number;
+ width?: number;
+ height?: number;
}
diff --git a/src/features/localization/dictionaries/images/en.tsx b/src/features/localization/dictionaries/images/en.tsx
index 3c571f8d..0eb49401 100644
--- a/src/features/localization/dictionaries/images/en.tsx
+++ b/src/features/localization/dictionaries/images/en.tsx
@@ -1,20 +1,35 @@
-import auth0Logo from "@/features/common/assets/auth0-logo.png";
-import jwtLogo from "@/features/common/assets/jwt-flower.png";
-import { ImagesDictionaryModel } from "@/features/localization/models/images-dictionary.model";
+import { BrandDictionaryModel } from "@/features/localization/models/images-dictionary.model";
-export const enImagesDictionary: ImagesDictionaryModel = {
- logos: {
- site: {
- src: jwtLogo.src,
- alt: "A token that resembles a flower with petals of a different color.",
- width: jwtLogo.width,
- height: jwtLogo.height,
+export const enBrandDictionary: BrandDictionaryModel = {
+ title: "Right-click or long-press for logo options",
+ menu: {
+ brand: {
+ label: "Brand",
+ items: [
+ {icon: "",
+ label: "Copy Logo SVG"
+ },
+ {icon: "",
+ label: "Download Logo"
+ },
+ {icon: "",
+ label: "Copy Symbol SVG"
+ },
+ {icon: "",
+ label: "Download Symbol"
+ },
+ {icon: "",
+ label: "Copy Wordmark SVG"
+ },
+ {icon: "",
+ label: "Download Wordmark"
+ },
+ ]
},
- auth0: {
- src: auth0Logo.src,
- alt: "This logo has the word “Auth0” and a shield on its left side. The shield has a four-pointed star inside, which spans across its surface.",
- width: auth0Logo.width,
- height: auth0Logo.height,
- },
- },
+ tools: {
+ label: "Tools",
+ items: [
+ ]
+ }
+ }
};
diff --git a/src/features/localization/dictionaries/images/ja.tsx b/src/features/localization/dictionaries/images/ja.tsx
index 112f37b3..42dc0ebb 100644
--- a/src/features/localization/dictionaries/images/ja.tsx
+++ b/src/features/localization/dictionaries/images/ja.tsx
@@ -1,20 +1,35 @@
-import auth0Logo from "@/features/common/assets/auth0-logo.png";
-import jwtLogo from "@/features/common/assets/jwt-flower.png";
-import { ImagesDictionaryModel } from "@/features/localization/models/images-dictionary.model";
+import { BrandDictionaryModel } from "@/features/localization/models/images-dictionary.model";
-export const jaImagesDictionary: ImagesDictionaryModel = {
- logos: {
- site: {
- src: jwtLogo.src,
- alt: "異なる色の花びらを持つ花に似たトークン。",
- width: jwtLogo.width,
- height: jwtLogo.height,
+export const jaImagesDictionary: BrandDictionaryModel = {
+ title: "Right-click or long-press for logo options",
+ menu: {
+ brand: {
+ label: "Brand",
+ items: [
+ {icon: "",
+ label: "Copy Logo SVG"
+ },
+ {icon: "",
+ label: "Download Logo"
+ },
+ {icon: "",
+ label: "Copy Symbol SVG"
+ },
+ {icon: "",
+ label: "Download Symbol"
+ },
+ {icon: "",
+ label: "Copy Wordmark SVG"
+ },
+ {icon: "",
+ label: "Download Wordmark"
+ },
+ ]
},
- auth0: {
- src: auth0Logo.src,
- alt: "“Auth0”ロゴ。盾の中央から四方八方に伸びる星が描かれている。",
- width: auth0Logo.width,
- height: auth0Logo.height,
- },
- },
+ tools: {
+ label: "Tools",
+ items: [
+ ]
+ }
+ }
};
diff --git a/src/features/localization/models/images-dictionary.model.ts b/src/features/localization/models/images-dictionary.model.ts
index 392e4e75..529fcde5 100644
--- a/src/features/localization/models/images-dictionary.model.ts
+++ b/src/features/localization/models/images-dictionary.model.ts
@@ -1,8 +1,17 @@
-import { StaticImageMetadataModel } from "@/features/common/models/static-image-metadata.model";
+interface BrandMenuItem {
+ icon: string;
+ label: string;
+}
+interface BrandMenuSection {
+ label: string;
+ items: BrandMenuItem[];
+}
+interface BrandMenu {
+ brand: BrandMenuSection;
+ tools: BrandMenuSection;
+}
-export interface ImagesDictionaryModel {
- logos: {
- site: StaticImageMetadataModel;
- auth0: StaticImageMetadataModel;
- };
+export interface BrandDictionaryModel {
+ title: string;
+ menu: BrandMenu;
}
diff --git a/src/features/localization/services/images-dictionary.service.tsx b/src/features/localization/services/images-dictionary.service.tsx
index acd51002..1a14acee 100644
--- a/src/features/localization/services/images-dictionary.service.tsx
+++ b/src/features/localization/services/images-dictionary.service.tsx
@@ -1,11 +1,11 @@
-import { ImagesDictionaryModel } from "@/features/localization/models/images-dictionary.model";
-import { enImagesDictionary } from "@/features/localization/dictionaries/images/en";
+import { BrandDictionaryModel } from "@/features/localization/models/images-dictionary.model";
+import { enBrandDictionary } from "@/features/localization/dictionaries/images/en";
import { jaImagesDictionary } from "@/features/localization/dictionaries/images/ja";
const imagesDictionaries: {
- [index: string]: ImagesDictionaryModel;
+ [index: string]: BrandDictionaryModel;
} = {
- en: enImagesDictionary,
+ en: enBrandDictionary,
ja: jaImagesDictionary,
};
diff --git a/src/libs/theme/styles/_variables.scss b/src/libs/theme/styles/_variables.scss
index e5a0bd15..ea9ad6a5 100644
--- a/src/libs/theme/styles/_variables.scss
+++ b/src/libs/theme/styles/_variables.scss
@@ -150,7 +150,7 @@ $gradient-app-card---resting: linear-gradient(
$ribbon-height: 2.5rem;
$ribbon-height-mobile: 2.5rem;
-$navbar-height: 3rem;
+$navbar-height: 5rem;
$navbar-height-mobile: 3rem;
$main-nav-height: calc($ribbon-height + $navbar-height);
$main-nav-height-mobile: calc($ribbon-height-mobile + $navbar-height-mobile);
diff --git a/src/libs/theme/styles/globals.scss b/src/libs/theme/styles/globals.scss
index 051ef32b..7c779af1 100644
--- a/src/libs/theme/styles/globals.scss
+++ b/src/libs/theme/styles/globals.scss
@@ -45,10 +45,11 @@
:root {
--color_bg_page: var(--functional-gray-950);
--color_fg_bold: var(--functional-gray-50);
- --color_fg_default: var(--functional-gray-200);
+ --color_fg_default: var(--functional-gray-500);
--color_fg_link_primary: var(--cloud);
- --color_bg_app_bar: rgba(17, 17, 17, 0.66);
+ --color_bg_app_bar: hsla(0,0%,7%,.04);
+ --color_bg_app_bar_border: hsla(0, 0%, 7%, .12);
--color_bg_app_bar_plain: rgba(17, 17, 17);
--color_fg_link: var(--functional-gray-50);
@@ -195,10 +196,8 @@ html[data-theme="system-light"] {
--color_bg_page: var(--functional-gray-0);
--color_fg_bold: var(--charcoal2);
- --color_fg_default: var(--functional-gray-650);
--color_fg_link_primary: var(--sky);
- --color_bg_app_bar: rgba(241, 241, 241, 0.66);
--color_bg_app_bar_plain: rgba(241, 241, 241);
--color_fg_link: var(--charcoal2);
From 79f7c8d44d5f643829403e5188e5b11c1ec91b1d Mon Sep 17 00:00:00 2001
From: Javier Tinoco <213990346+javiert-okta@users.noreply.github.com>
Date: Mon, 15 Sep 2025 11:13:27 -0500
Subject: [PATCH 033/107] delete unused prop
---
.../components/debugger-picker/debugger-picker.component.tsx | 2 --
1 file changed, 2 deletions(-)
diff --git a/src/features/common/components/debugger-picker/debugger-picker.component.tsx b/src/features/common/components/debugger-picker/debugger-picker.component.tsx
index aa28f50e..450e9619 100644
--- a/src/features/common/components/debugger-picker/debugger-picker.component.tsx
+++ b/src/features/common/components/debugger-picker/debugger-picker.component.tsx
@@ -55,7 +55,6 @@ export const DebuggerPickerComponent: React.FC<
label,
options,
handleSelection,
- selectedOptionCode,
placeholder,
minWidth,
}) => {
@@ -89,7 +88,6 @@ export const DebuggerPickerComponent: React.FC<
aria-label={"Debugger picker"}
className="react-select-container"
onChange={handleChange}
- //value={selectedOptionCode}
options={options}
menuPortalTarget={document.body}
classNamePrefix={"react-select"}
From 2993e82b16fc41daf3883afe72414fbf3ebfcb95 Mon Sep 17 00:00:00 2001
From: Javier Tinoco <213990346+javiert-okta@users.noreply.github.com>
Date: Mon, 15 Sep 2025 11:13:44 -0500
Subject: [PATCH 034/107] update focused and disabled styles
---
src/libs/theme/styles/globals.scss | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/libs/theme/styles/globals.scss b/src/libs/theme/styles/globals.scss
index 8bd4bf58..4b320253 100644
--- a/src/libs/theme/styles/globals.scss
+++ b/src/libs/theme/styles/globals.scss
@@ -523,8 +523,9 @@ $picker-list-offset-lg: calc(($picker-list-width-lg - $picker-width-lg) / 2);
}
.react-select__control--is-focused {
- border-color: var(--color_border_focus) !important;
- box-shadow: 0 0 0 1px var(--color_border_focus) !important;
+ transition: 0.2s ease-out;
+ border-color: var(--color_border_default) !important;
+ box-shadow: 0 0 0 3px var(--color_border_default) !important;
}
.react-select__value-container {
@@ -637,7 +638,7 @@ $picker-list-offset-lg: calc(($picker-list-width-lg - $picker-width-lg) / 2);
.react-select__option--is-disabled {
color: var(--color_fg_bold) !important;
font-weight: 500;
- opacity: 0.32;
+ opacity: 0.32 !important;
cursor: not-allowed !important;
}
From e442a80e7a29975c6a26cba5f0158cb7cf6bbc2c Mon Sep 17 00:00:00 2001
From: Javier Tinoco <213990346+javiert-okta@users.noreply.github.com>
Date: Tue, 16 Sep 2025 12:35:30 -0500
Subject: [PATCH 035/107] add new encoding toggle switch component
---
.../encoding-format-toggle-switch.module.scss | 82 +++++++++++++++++++
.../encoding-format-toggle-switch.tsx | 42 ++++++++++
2 files changed, 124 insertions(+)
create mode 100644 src/features/decoder/components/encoding-format-toggle-swith/encoding-format-toggle-switch.module.scss
create mode 100644 src/features/decoder/components/encoding-format-toggle-swith/encoding-format-toggle-switch.tsx
diff --git a/src/features/decoder/components/encoding-format-toggle-swith/encoding-format-toggle-switch.module.scss b/src/features/decoder/components/encoding-format-toggle-swith/encoding-format-toggle-switch.module.scss
new file mode 100644
index 00000000..377661de
--- /dev/null
+++ b/src/features/decoder/components/encoding-format-toggle-swith/encoding-format-toggle-switch.module.scss
@@ -0,0 +1,82 @@
+@use "@/libs/theme/styles/variables" as *;
+
+.container {
+ display: flex;
+ align-items: center;
+ gap: .5rem;
+}
+
+.label {
+ display: flex;
+ flex-direction: column;
+ text-transform: uppercase;
+ font-size: .75rem;
+ line-height: 1.25rem;
+ color: var(--color_fg_default);
+ letter-spacing: .1px;
+}
+
+.fullLabel {
+ display: none;
+ font-size: .8125rem;
+ font-weight: 500;
+ @media #{$breakpoint-dimension-sm} {
+ display: unset;
+ }
+}
+
+.input {
+ opacity: 0;
+ width: 0;
+ height: 0;
+}
+
+.switch__container {
+ position: relative;
+ display: inline-block;
+ width: 40px;
+ height: 24px;
+ transition: all .25s cubic-bezier(.17,.67,.83,.67);
+}
+
+.picker__round {
+ border-radius: 24px;
+ &::before {
+ border-radius: 50%;
+ }
+}
+
+.picker__slider {
+ position: absolute;
+ cursor: pointer;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: var(--color_bg_layer_bold);
+ border: 1px solid var(--color_border_default);
+ box-shadow: 0 0 1px rgba(0,0,0,.06),0 0 1px rgba(0,0,0,.06),inset 0 1px 1px .5px rgba(0,0,0,.04);
+ transition: background-color .25s cubic-bezier(.17,.67,.83,.67),border .25s ease;
+ &::before {
+ position: absolute;
+ content: "";
+ height: 16px;
+ width: 16px;
+ top: 3px;
+ left: 3px;
+ background: linear-gradient(180deg,transparent,rgba(0,0,0,.08)),#fff;
+ box-shadow: 0 2px 2px -1px rgba(0,0,0,.2),0 2px 4px -2px rgba(0,0,0,.2),inset 0 .5px .5px hsla(0,0%,100%,.12);
+ transition: transform .25s cubic-bezier(.34,1.56,.64,1),background-color .2s ease;
+ will-change: transform;
+ }
+}
+
+input:checked+.picker__slider {
+ background-color: var(--color_fg_bold);
+ border: 1px solid var(--color_fg_bold);
+ &::before {
+ transform: translateX(16px);
+ background-color: var(--color_fg_on_state_success);
+ }
+}
+
diff --git a/src/features/decoder/components/encoding-format-toggle-swith/encoding-format-toggle-switch.tsx b/src/features/decoder/components/encoding-format-toggle-swith/encoding-format-toggle-switch.tsx
new file mode 100644
index 00000000..5d29db85
--- /dev/null
+++ b/src/features/decoder/components/encoding-format-toggle-swith/encoding-format-toggle-switch.tsx
@@ -0,0 +1,42 @@
+import React, { ChangeEvent, useEffect, useState } from "react";
+import styles from "./encoding-format-toggle-switch.module.scss";
+import { EncodingValues } from "@/features/common/values/encoding.values";
+import { useDecoderStore } from "@/features/decoder/services/decoder.store";
+import { getPickersUiDictionary } from "@/features/localization/services/ui-language-dictionary.service";
+import { Switch } from "react-aria-components";
+import clsx from "clsx";
+
+interface EncodingFormatToggleSwitchComponentProps {
+ languageCode: string;
+}
+
+export const EncodingFormatToggleSwitchComponent: React.FC<
+ EncodingFormatToggleSwitchComponentProps
+> = ({ languageCode }) => {
+ const dictionary = getPickersUiDictionary(languageCode);
+
+ const handleSymmetricSecretKeyEncodingChange = useDecoderStore(
+ (state) => state.handleSymmetricSecretKeyEncodingChange
+ );
+
+ const onSecretEncodingFormatChange = (event: ChangeEvent) => {
+ handleSymmetricSecretKeyEncodingChange(event.target.checked ? EncodingValues.BASE64URL : EncodingValues.UTF8);
+ };
+
+ return (
+
+
+ Base64URL Encoded?
+
+
+
+ );
+};
From cd270fcaa7eaecf23b282cdebcb8c2c4cadc7792 Mon Sep 17 00:00:00 2001
From: Javier Tinoco <213990346+javiert-okta@users.noreply.github.com>
Date: Tue, 16 Sep 2025 12:36:50 -0500
Subject: [PATCH 036/107] update card styles and component to render toggle
switch
---
.../common/components/card/card.component.tsx | 50 ++++++++++---------
.../common/components/card/card.module.scss | 12 +++++
2 files changed, 39 insertions(+), 23 deletions(-)
diff --git a/src/features/common/components/card/card.component.tsx b/src/features/common/components/card/card.component.tsx
index 504f2df9..739482af 100644
--- a/src/features/common/components/card/card.component.tsx
+++ b/src/features/common/components/card/card.component.tsx
@@ -1,10 +1,11 @@
import React, { PropsWithChildren, useId } from "react";
import styles from "./card.module.scss";
import { clsx } from "clsx";
-import { getLocalizedSecondaryFont, MonoFont } from "@/libs/theme/fonts";
+import { getLocalizedSecondaryFont } from "@/libs/theme/fonts";
import { CardMessageComponent } from "@/features/common/components/card-message/card-message.component";
import { HeaderIcon } from "../icons/header/header-icon";
import { CheckIcon } from "../icons/check/check-icon";
+import { EncodingFormatToggleSwitchComponent } from "@/features/decoder/components/encoding-format-toggle-swith/encoding-format-toggle-switch";
export interface CardComponentProps extends PropsWithChildren {
id: string;
@@ -151,8 +152,8 @@ export const CardComponent: React.FC = (props) => {
{messages.success.map((line, index) => {
return (
<>
-
- {line}
+
+ {line}
>
);
})}
@@ -201,27 +202,30 @@ export const CardWithHeadlineComponent: React.FC<
return (
{sectionHeadline && (
- <>
-
- {sectionHeadline.title}
- {sectionHeadline.titleTag && (
-
- {sectionHeadline.titleTag}
-
+
+
+
+ {sectionHeadline.title}
+ {sectionHeadline.titleTag && (
+
+ {sectionHeadline.titleTag}
+
+ )}
+
+ {sectionHeadline.description && (
+
+ {sectionHeadline.description}
+
)}
-
- {sectionHeadline.description && (
-
- {sectionHeadline.description}
-
- )}
- >
+
+
+
)}
diff --git a/src/features/common/components/card/card.module.scss b/src/features/common/components/card/card.module.scss
index 382be7c2..5472c0c4 100644
--- a/src/features/common/components/card/card.module.scss
+++ b/src/features/common/components/card/card.module.scss
@@ -56,6 +56,18 @@
}
}
+.title__container {
+ grid-column: 1/-1;
+ width: 100%;
+ color: var(--color_fg_bold);
+ font-size: 1.75rem;
+ font-weight: 500;
+ line-height: 1.5;
+ margin-bottom: .5rem;
+ display: flex;
+ justify-content: space-between;
+}
+
.card {
isolation: isolate;
grid-column: span 6;
From f4624e307a825c4a729ccdb3baa0c1d0b10bf671 Mon Sep 17 00:00:00 2001
From: Javier Tinoco <213990346+javiert-okta@users.noreply.github.com>
Date: Tue, 16 Sep 2025 12:36:59 -0500
Subject: [PATCH 037/107] comment footer
---
.../components/secret-key-input.component.tsx | 26 ++++++++++++-------
1 file changed, 16 insertions(+), 10 deletions(-)
diff --git a/src/features/decoder/components/secret-key-input.component.tsx b/src/features/decoder/components/secret-key-input.component.tsx
index 919a6f88..3a852cd7 100644
--- a/src/features/decoder/components/secret-key-input.component.tsx
+++ b/src/features/decoder/components/secret-key-input.component.tsx
@@ -19,6 +19,7 @@ import { CardToolbarComponent } from "@/features/common/components/card-toolbar/
import { CardToolbarCopyButtonComponent } from "@/features/common/components/card-toolbar-buttons/card-toolbar-copy-button/card-toolbar-copy-button.component";
import { CardToolbarClearButtonComponent } from "@/features/common/components/card-toolbar-buttons/card-toolbar-clear-button/card-toolbar-clear-button.component";
import { NOOP_ALG } from "@/features/common/values/constants";
+import { EncodingFormatToggleSwitchComponent } from "./encoding-format-toggle-swith/encoding-format-toggle-switch";
type SecretKeyInputComponentProps = {
languageCode: string;
@@ -29,27 +30,27 @@ export const SecretKeyInputComponent: React.FC<
SecretKeyInputComponentProps
> = ({ languageCode, dictionary }) => {
const handleSymmetricSecretKeyChange$ = useDecoderStore(
- (state) => state.handleSymmetricSecretKeyChange,
+ (state) => state.handleSymmetricSecretKeyChange
);
const handleAsymmetricPublicKeyChange$ = useDecoderStore(
- (state) => state.handleAsymmetricPublicKeyChange,
+ (state) => state.handleAsymmetricPublicKeyChange
);
const resetControlledSymmetricSecretKey$ = useDecoderStore(
- (state) => state.resetControlledSymmetricSecretKey,
+ (state) => state.resetControlledSymmetricSecretKey
);
const resetControlledAsymmetricPublicKey$ = useDecoderStore(
- (state) => state.resetControlledAsymmetricPublicKey,
+ (state) => state.resetControlledAsymmetricPublicKey
);
const alg$ = useDecoderStore((state) => state.alg);
const verificationInputErrors$ = useDecoderStore(
- (state) => state.verificationInputErrors,
+ (state) => state.verificationInputErrors
);
const controlledSymmetricSecretKey = useDecoderStore(
- (state) => state.controlledSymmetricSecretKey,
+ (state) => state.controlledSymmetricSecretKey
);
const controlledAsymmetricPublicKey = useDecoderStore(
- (state) => state.controlledAsymmetricPublicKey,
+ (state) => state.controlledAsymmetricPublicKey
);
const decoderInputs$ = useDebuggerStore((state) => state.decoderInputs$);
@@ -58,7 +59,7 @@ export const SecretKeyInputComponent: React.FC<
decoderInputs$.algType === SigningAlgCategoryValues.SYMMETRIC &&
decoderInputs$.symmetricSecretKey
? decoderInputs$.symmetricSecretKey
- : DEFAULT_JWT.secret,
+ : DEFAULT_JWT.secret
);
const [publicKey, setPublicKey] = useState("");
@@ -87,7 +88,7 @@ export const SecretKeyInputComponent: React.FC<
};
const handleSymmetricSecretKeyChange = async (
- e: ChangeEvent,
+ e: ChangeEvent
) => {
const key = e.target.value;
@@ -99,7 +100,7 @@ export const SecretKeyInputComponent: React.FC<
};
const handleAsymmetricPublicKeyChange = async (
- e: ChangeEvent,
+ e: ChangeEvent
) => {
const key = e.target.value;
@@ -162,6 +163,11 @@ export const SecretKeyInputComponent: React.FC<
/>
),
+ /* footer: isHmacAlg(alg$) ? (
+
+ ) : (
+
+ ), */
}}
>
{isHmacAlg(alg$) && (
From ff3738173569a6822a7a46d4dfc5f2a8c4b481a7 Mon Sep 17 00:00:00 2001
From: Javier Tinoco <213990346+javiert-okta@users.noreply.github.com>
Date: Tue, 16 Sep 2025 12:37:35 -0500
Subject: [PATCH 038/107] format secret key input component
---
.../components/secret-key-input.component.tsx | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/src/features/decoder/components/secret-key-input.component.tsx b/src/features/decoder/components/secret-key-input.component.tsx
index 3a852cd7..0cb09328 100644
--- a/src/features/decoder/components/secret-key-input.component.tsx
+++ b/src/features/decoder/components/secret-key-input.component.tsx
@@ -30,27 +30,27 @@ export const SecretKeyInputComponent: React.FC<
SecretKeyInputComponentProps
> = ({ languageCode, dictionary }) => {
const handleSymmetricSecretKeyChange$ = useDecoderStore(
- (state) => state.handleSymmetricSecretKeyChange
+ (state) => state.handleSymmetricSecretKeyChange,
);
const handleAsymmetricPublicKeyChange$ = useDecoderStore(
- (state) => state.handleAsymmetricPublicKeyChange
+ (state) => state.handleAsymmetricPublicKeyChange,
);
const resetControlledSymmetricSecretKey$ = useDecoderStore(
- (state) => state.resetControlledSymmetricSecretKey
+ (state) => state.resetControlledSymmetricSecretKey,
);
const resetControlledAsymmetricPublicKey$ = useDecoderStore(
- (state) => state.resetControlledAsymmetricPublicKey
+ (state) => state.resetControlledAsymmetricPublicKey,
);
const alg$ = useDecoderStore((state) => state.alg);
const verificationInputErrors$ = useDecoderStore(
- (state) => state.verificationInputErrors
+ (state) => state.verificationInputErrors,
);
const controlledSymmetricSecretKey = useDecoderStore(
- (state) => state.controlledSymmetricSecretKey
+ (state) => state.controlledSymmetricSecretKey,
);
const controlledAsymmetricPublicKey = useDecoderStore(
- (state) => state.controlledAsymmetricPublicKey
+ (state) => state.controlledAsymmetricPublicKey,
);
const decoderInputs$ = useDebuggerStore((state) => state.decoderInputs$);
@@ -88,7 +88,7 @@ export const SecretKeyInputComponent: React.FC<
};
const handleSymmetricSecretKeyChange = async (
- e: ChangeEvent
+ e: ChangeEvent,
) => {
const key = e.target.value;
@@ -100,7 +100,7 @@ export const SecretKeyInputComponent: React.FC<
};
const handleAsymmetricPublicKeyChange = async (
- e: ChangeEvent
+ e: ChangeEvent,
) => {
const key = e.target.value;
From 19565b7fc3ace0b8ddf9cfe6ae9456be3b1d56fa Mon Sep 17 00:00:00 2001
From: Javier Tinoco <213990346+javiert-okta@users.noreply.github.com>
Date: Wed, 17 Sep 2025 12:07:11 -0500
Subject: [PATCH 039/107] update react control styles
---
src/libs/theme/styles/globals.scss | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/libs/theme/styles/globals.scss b/src/libs/theme/styles/globals.scss
index 4b320253..837baf4f 100644
--- a/src/libs/theme/styles/globals.scss
+++ b/src/libs/theme/styles/globals.scss
@@ -507,7 +507,7 @@ $picker-list-offset-lg: calc(($picker-list-width-lg - $picker-width-lg) / 2);
background: transparent;
color: var(--color_fg_bold);
font-family: var(--font-primary), monospace !important;
- height: 2rem !important;
+ height: 2rem;
}
.react-select__control {
@@ -557,7 +557,7 @@ $picker-list-offset-lg: calc(($picker-list-width-lg - $picker-width-lg) / 2);
border: 1px solid var(--color_border_default);
padding: 0.25rem;
overflow: hidden;
- min-width: 240px;
+ min-width: 6.125rem;
}
.react-select__menu-portal {
From deb9bfc2ece08bb5b92e41188a75a8a3d8c2a724 Mon Sep 17 00:00:00 2001
From: Javier Tinoco <213990346+javiert-okta@users.noreply.github.com>
Date: Wed, 17 Sep 2025 12:08:29 -0500
Subject: [PATCH 040/107] move pickers to card component
---
.../common/components/card/card.component.tsx | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/src/features/common/components/card/card.component.tsx b/src/features/common/components/card/card.component.tsx
index 739482af..885c6f1b 100644
--- a/src/features/common/components/card/card.component.tsx
+++ b/src/features/common/components/card/card.component.tsx
@@ -6,6 +6,9 @@ import { CardMessageComponent } from "@/features/common/components/card-message/
import { HeaderIcon } from "../icons/header/header-icon";
import { CheckIcon } from "../icons/check/check-icon";
import { EncodingFormatToggleSwitchComponent } from "@/features/decoder/components/encoding-format-toggle-swith/encoding-format-toggle-switch";
+import { useDecoderStore } from "@/features/decoder/services/decoder.store";
+import { isHmacAlg } from "../../services/jwt.service";
+import { TokenDecoderKeyFormatPickerComponent } from "@/features/decoder/components/token-decoder-key-format-picker.component";
export interface CardComponentProps extends PropsWithChildren {
id: string;
@@ -151,10 +154,10 @@ export const CardComponent: React.FC = (props) => {
>
{messages.success.map((line, index) => {
return (
- <>
+
{line}
- >
+
);
})}
@@ -198,6 +201,7 @@ export const CardWithHeadlineComponent: React.FC<
CardWithHeadlineComponentProps
> = ({ sectionHeadline, languageCode, ...props }) => {
const regionId = useId();
+ const alg$ = useDecoderStore((state) => state.alg);
return (
@@ -224,7 +228,12 @@ export const CardWithHeadlineComponent: React.FC<
)}
-
+
+ {isHmacAlg(alg$) ? (
+
+ ) : (
+
+ )}