diff --git a/e2e/decoder.spec.ts b/e2e/decoder.spec.ts index dc6e7231..c9b367bf 100644 --- a/e2e/decoder.spec.ts +++ b/e2e/decoder.spec.ts @@ -27,6 +27,7 @@ import { import { MessageStatusValue, MessageTypeValue } from "./e2e.values"; import { JwtDictionaryModel, JwtSignedWithDigitalModel } from "./e2e.models"; import jwts from "./jwt.json" with { type: "json" }; +import { EncodingValues } from "@/features/common/values/encoding.values"; const TestJwts = (jwts as JwtDictionaryModel).byAlgorithm; @@ -39,7 +40,7 @@ test.describe("Can interact with JWT Decoder JWT editor", () => { const jwtEditorInput = getDecoderJwtEditorInput(page); await expect(jwtEditorInput).toHaveValue( - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30", + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30" ); }); @@ -53,8 +54,13 @@ test.describe("Can interact with JWT Decoder JWT editor", () => { await expect(jwtEditorInput).toHaveValue(inputValue); }); - test("can copy value in JWT editor", async ({ page, context, browserName }) => { - const permissions = browserName === 'firefox' ? [] : ["clipboard-read", "clipboard-write"] + test("can copy value in JWT editor", async ({ + page, + context, + browserName, + }) => { + const permissions = + browserName === "firefox" ? [] : ["clipboard-read", "clipboard-write"]; const inputValue = (TestJwts.RS512 as JwtSignedWithDigitalModel).withPemKey .jwt; await context.grantPermissions(permissions); @@ -76,7 +82,7 @@ test.describe("Can interact with JWT Decoder JWT editor", () => { await copyButton.click(); const clipboardContent = await page.evaluate(() => - navigator.clipboard.readText(), + navigator.clipboard.readText() ); expect(clipboardContent).toBe(inputValue); @@ -105,51 +111,14 @@ test.describe("Can generate JWT examples", () => { await page.goto(E2E_BASE_URL); }); - test("Can open and close JWT Decoder example widget", async ({ page }) => { - const lang = await getLang(page); - expectToBeNonNull(lang); - - const pickersUiDictionary = getPickersUiDictionary(lang); - - const decoderWidget = page.getByTestId(dataTestidDictionary.decoder.id); - - const exampleButton = decoderWidget.getByRole("button", { - name: pickersUiDictionary.exampleAlgPicker.label, - }); - - await exampleButton.click(); - - await expect(exampleButton).not.toBeVisible(); - - const closeButton = page.getByRole("button", { - name: pickersUiDictionary.exampleAlgPicker.closeButton.label, - }); - - await closeButton.click(); - - await expect(exampleButton).toBeVisible(); - await expect(closeButton).not.toBeVisible(); - }); - test.describe("Can generate a JWT decoder example", () => { test.beforeEach(async ({ page }) => { const lang = await getLang(page); expectToBeNonNull(lang); const pickersUiDictionary = getPickersUiDictionary(lang); - - const decoderWidget = page.getByTestId(dataTestidDictionary.decoder.id); - - const exampleButton = decoderWidget.getByRole("button", { - name: pickersUiDictionary.exampleAlgPicker.label, - }); - - await exampleButton.click(); - - await expect(exampleButton).not.toBeVisible(); - - const pickerIndicator = decoderWidget.getByText( - pickersUiDictionary.exampleAlgPicker.defaultValue, + const pickerIndicator = page.getByText( + pickersUiDictionary.exampleAlgPicker.defaultValue ); await pickerIndicator.click(); @@ -174,7 +143,7 @@ test.describe("Can generate JWT examples", () => { const targetToken = DefaultTokensValues[option]; const jwtEditor = decoderWidget.getByTestId( - dataTestidDictionary.decoder.jwtEditor.id, + dataTestidDictionary.decoder.jwtEditor.id ); const jwtEditorInput = jwtEditor.getByRole("textbox"); @@ -203,7 +172,7 @@ test.describe("Can generate JWT examples", () => { }); const secretKeyEditor = decoderWidget.getByTestId( - dataTestidDictionary.decoder.secretKeyEditor.id, + dataTestidDictionary.decoder.secretKeyEditor.id ); const secretKeyInput = secretKeyEditor.getByRole("textbox"); @@ -217,9 +186,13 @@ test.describe("Can generate JWT examples", () => { status: MessageStatusValue.VISIBLE, }); - const encodingValue = await secretKeyEditor - .locator(".react-select__single-value") - .innerText(); + const isEncodingSwitchChecked = await page + .getByRole("switch") + .isChecked(); + + const encodingValue = isEncodingSwitchChecked + ? EncodingValues.BASE64URL + : EncodingValues.UTF8; expect(encodingValue).toBe(symmetricToken.secretEncoding); @@ -236,8 +209,9 @@ test.describe("Can generate JWT examples", () => { status: MessageStatusValue.VISIBLE, }); - const formatValue = await secretKeyEditor + const formatValue = await page .locator(".react-select__single-value") + .nth(1) .innerText(); expect(formatValue).toBe(asymmetricToken.publicKeyFormat); @@ -270,7 +244,7 @@ test.describe("decode JWTs", () => { const decoderWidget = page.getByTestId(dataTestidDictionary.decoder.id); const jwtEditor = decoderWidget.getByTestId( - dataTestidDictionary.decoder.jwtEditor.id, + dataTestidDictionary.decoder.jwtEditor.id ); const jwtEditorInput = jwtEditor.getByRole("textbox"); @@ -327,14 +301,14 @@ test.describe("decode JWTs", () => { }); const secretKeyEditor = decoderWidget.getByTestId( - dataTestidDictionary.decoder.secretKeyEditor.id, + dataTestidDictionary.decoder.secretKeyEditor.id ); const secretKeyEditorInput = secretKeyEditor.getByRole("textbox"); await secretKeyEditorInput.fill(entryWithUtf8Secret.secret); await expect(secretKeyEditorInput).toHaveValue( - entryWithUtf8Secret.secret, + entryWithUtf8Secret.secret ); await checkSecretKeyDecoderEditorStatusBarMessage({ @@ -343,9 +317,13 @@ test.describe("decode JWTs", () => { status: MessageStatusValue.VISIBLE, }); - const encodingValue = await secretKeyEditor - .locator(".react-select__single-value") - .innerText(); + const isEncodingSwitchChecked = await page + .getByRole("switch") + .isChecked(); + + const encodingValue = isEncodingSwitchChecked + ? EncodingValues.BASE64URL + : EncodingValues.UTF8; expect(encodingValue).toBe(entryWithUtf8Secret.secretEncoding); @@ -388,17 +366,13 @@ test.describe("decode JWTs", () => { status: MessageStatusValue.VISIBLE, }); - const formatPicker = secretKeyEditor.locator( - ".react-select__single-value", - ); - - await formatPicker.click(); + const encodingSwitch = await page.getByTestId(dataTestidDictionary.encoder.switch) + expect(encodingSwitch).toBeVisible() - await page - .getByRole("option", { - name: entryWithBase64urlSecret.secretEncoding, - }) - .click(); + if ( + entryWithBase64urlSecret.secretEncoding === EncodingValues.BASE64URL + ) + await encodingSwitch.click(); await secretKeyEditorInput.fill(entryWithBase64urlSecret.secret); @@ -451,7 +425,7 @@ test.describe("decode JWTs", () => { }); const secretKeyEditor = decoderWidget.getByTestId( - dataTestidDictionary.decoder.secretKeyEditor.id, + dataTestidDictionary.decoder.secretKeyEditor.id ); const secretKeyEditorInput = secretKeyEditor.getByRole("textbox"); @@ -465,7 +439,7 @@ test.describe("decode JWTs", () => { status: MessageStatusValue.VISIBLE, }); - const formatValue = await secretKeyEditor + const formatValue = await page .locator(".react-select__single-value") .innerText(); @@ -511,8 +485,8 @@ test.describe("decode JWTs", () => { status: MessageStatusValue.VISIBLE, }); - const formatPicker = secretKeyEditor.locator( - ".react-select__single-value", + const formatPicker = page.locator( + ".react-select__single-value" ); await formatPicker.click(); @@ -530,7 +504,7 @@ test.describe("decode JWTs", () => { }); await secretKeyEditorInput.fill( - JSON.stringify(entrywithJwkKey.publicKey, null, 2), + JSON.stringify(entrywithJwkKey.publicKey, null, 2) ); await checkJwtEditorStatusBarMessage({ @@ -581,8 +555,8 @@ test.describe("decode JWTs", () => { status: MessageStatusValue.VISIBLE, }); - const formatPicker = secretKeyEditor.locator( - ".react-select__single-value", + const formatPicker = page.locator( + ".react-select__single-value" ); await formatPicker.click(); @@ -594,7 +568,7 @@ test.describe("decode JWTs", () => { .click(); await secretKeyEditorInput.fill( - JSON.stringify(entrywithJwkKey.publicKey, null, 2), + JSON.stringify(entrywithJwkKey.publicKey, null, 2) ); await checkJwtEditorStatusBarMessage({ @@ -642,10 +616,10 @@ test("Can decode JWTs signed with a non-supported algorithm", async ({ kid: "sig", }; const expectedDecodedPayload = { - "sub": "1234567890", - "name": "John Doe", - "admin": true, - "iat": 1516239022 + sub: "1234567890", + name: "John Doe", + admin: true, + iat: 1516239022, }; await page.goto(E2E_BASE_URL); @@ -656,7 +630,7 @@ test("Can decode JWTs signed with a non-supported algorithm", async ({ const decoderWidget = page.getByTestId(dataTestidDictionary.decoder.id); const jwtEditor = decoderWidget.getByTestId( - dataTestidDictionary.decoder.jwtEditor.id, + dataTestidDictionary.decoder.jwtEditor.id ); const jwtEditorInput = jwtEditor.getByRole("textbox"); @@ -678,22 +652,22 @@ test("Can decode JWTs signed with a non-supported algorithm", async ({ await expect( decoderWidget.getByTestId( - dataTestidDictionary.decoder.decodedHeader.json.id, - ), + dataTestidDictionary.decoder.decodedHeader.json.id + ) ).toHaveText(JSON.stringify(expectedDecodedHeader, null, 2), { useInnerText: true, }); await expect( decoderWidget.getByTestId( - dataTestidDictionary.decoder.decodedPayload.json.id, - ), + dataTestidDictionary.decoder.decodedPayload.json.id + ) ).toHaveText(JSON.stringify(expectedDecodedPayload, null, 2), { useInnerText: true, }); const secretKeyEditor = decoderWidget.getByTestId( - dataTestidDictionary.decoder.secretKeyEditor.id, + dataTestidDictionary.decoder.secretKeyEditor.id ); await expect(secretKeyEditor).not.toBeVisible(); @@ -711,10 +685,10 @@ test.describe("Decode pieces of JWTs in Base64Url", () => { jwtPiece: "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0", expectedDecodedOutput: { - "sub": "1234567890", - "name": "John Doe", - "admin": true, - "iat": 1516239022 + sub: "1234567890", + name: "John Doe", + admin: true, + iat: 1516239022, }, }, { @@ -737,7 +711,7 @@ test.describe("Decode pieces of JWTs in Base64Url", () => { const decoderWidget = page.getByTestId(dataTestidDictionary.decoder.id); const jwtEditor = decoderWidget.getByTestId( - dataTestidDictionary.decoder.jwtEditor.id, + dataTestidDictionary.decoder.jwtEditor.id ); const jwtEditorInput = jwtEditor.getByRole("textbox"); @@ -759,20 +733,20 @@ test.describe("Decode pieces of JWTs in Base64Url", () => { await expect( decoderWidget.getByTestId( - dataTestidDictionary.decoder.decodedHeader.json.id, - ), + dataTestidDictionary.decoder.decodedHeader.json.id + ) ).toHaveText(JSON.stringify(expectedDecodedOutput, null, 2), { useInnerText: true, }); await expect( decoderWidget.getByTestId( - dataTestidDictionary.decoder.decodedPayload.json.id, - ), + dataTestidDictionary.decoder.decodedPayload.json.id + ) ).toBeEmpty(); const secretKeyEditor = decoderWidget.getByTestId( - dataTestidDictionary.decoder.secretKeyEditor.id, + dataTestidDictionary.decoder.secretKeyEditor.id ); await expect(secretKeyEditor).not.toBeVisible(); diff --git a/e2e/encoder.spec.ts b/e2e/encoder.spec.ts index 1bf35f1c..777ddad1 100644 --- a/e2e/encoder.spec.ts +++ b/e2e/encoder.spec.ts @@ -27,6 +27,7 @@ import { isNoneAlg, } from "@/features/common/services/jwt.service"; import { MessageStatusValue, MessageTypeValue } from "./e2e.values"; +import { EncodingValues } from "@/features/common/values/encoding.values"; const TestJwts = (jwts as JwtDictionaryModel).byAlgorithm; @@ -41,7 +42,7 @@ test.describe("Can interact with header editor in JWT Encoder", () => { const encoderWidget = page.getByTestId(dataTestidDictionary.encoder.id); const headerEditor = encoderWidget.getByTestId( - dataTestidDictionary.encoder.headerEditor.id, + dataTestidDictionary.encoder.headerEditor.id ); const headerEditorInput = headerEditor.getByRole("textbox"); @@ -65,7 +66,7 @@ test.describe("Can interact with header editor in JWT Encoder", () => { const encoderWidget = page.getByTestId(dataTestidDictionary.encoder.id); const headerEditor = encoderWidget.getByTestId( - dataTestidDictionary.encoder.headerEditor.id, + dataTestidDictionary.encoder.headerEditor.id ); const headerEditorInput = headerEditor.getByRole("textbox"); @@ -83,7 +84,7 @@ test.describe("Can interact with header editor in JWT Encoder", () => { const encoder = page.getByTestId(dataTestidDictionary.encoder.id); const headerEditor = encoder.getByTestId( - dataTestidDictionary.encoder.headerEditor.id, + dataTestidDictionary.encoder.headerEditor.id ); const headerEditorInput = headerEditor.getByRole("textbox"); @@ -107,7 +108,7 @@ test.describe("can interact with payload editor in JWT encoder", () => { const encoderWidget = page.getByTestId(dataTestidDictionary.encoder.id); const payloadEditor = encoderWidget.getByTestId( - dataTestidDictionary.encoder.payloadEditor.id, + dataTestidDictionary.encoder.payloadEditor.id ); const payloadEditorInput = payloadEditor.getByRole("textbox"); @@ -131,7 +132,7 @@ test.describe("can interact with payload editor in JWT encoder", () => { const encoderWidget = page.getByTestId(dataTestidDictionary.encoder.id); const payloadEditor = encoderWidget.getByTestId( - dataTestidDictionary.encoder.payloadEditor.id, + dataTestidDictionary.encoder.payloadEditor.id ); const payloadEditorInput = payloadEditor.getByRole("textbox"); @@ -149,7 +150,7 @@ test.describe("can interact with payload editor in JWT encoder", () => { const encoder = page.getByTestId(dataTestidDictionary.encoder.id); const payloadEditor = encoder.getByTestId( - dataTestidDictionary.encoder.payloadEditor.id, + dataTestidDictionary.encoder.payloadEditor.id ); const payloadEditorInput = payloadEditor.getByRole("textbox"); @@ -173,7 +174,7 @@ test.describe("can interact with secret editor in JWT encoder", () => { const encoderWidget = page.getByTestId(dataTestidDictionary.encoder.id); const secretKeyEditor = encoderWidget.getByTestId( - dataTestidDictionary.encoder.secretKeyEditor.id, + dataTestidDictionary.encoder.secretKeyEditor.id ); const secretKeyEditorInput = secretKeyEditor.getByRole("textbox"); @@ -191,7 +192,7 @@ test.describe("can interact with secret editor in JWT encoder", () => { const encoderWidget = page.getByTestId(dataTestidDictionary.encoder.id); const secretKeyEditor = encoderWidget.getByTestId( - dataTestidDictionary.encoder.secretKeyEditor.id, + dataTestidDictionary.encoder.secretKeyEditor.id ); const secretKeyEditorInput = secretKeyEditor.getByRole("textbox"); @@ -209,7 +210,7 @@ test.describe("can interact with secret editor in JWT encoder", () => { const encoder = page.getByTestId(dataTestidDictionary.encoder.id); const secretKeyEditor = encoder.getByTestId( - dataTestidDictionary.encoder.secretKeyEditor.id, + dataTestidDictionary.encoder.secretKeyEditor.id ); const secretKeyEditorInput = secretKeyEditor.getByRole("textbox"); @@ -229,32 +230,6 @@ test.describe("Generate JWT encoding examples", () => { await switchToEncoderTab(page); }); - test("Can open and close encoder example widget", async ({ page }) => { - const lang = await getLang(page); - expectToBeNonNull(lang); - - const pickersUiDictionary = getPickersUiDictionary(lang); - - const encoder = page.getByTestId(dataTestidDictionary.encoder.id); - - const exampleButton = encoder.getByRole("button", { - name: pickersUiDictionary.exampleAlgPicker.label, - }); - - await exampleButton.click(); - - await expect(exampleButton).not.toBeVisible(); - - const closeButton = page.getByRole("button", { - name: pickersUiDictionary.exampleAlgPicker.closeButton.label, - }); - - await closeButton.click(); - - await expect(exampleButton).toBeVisible(); - await expect(closeButton).not.toBeVisible(); - }); - test.describe("Can generate a JWT example", () => { test.beforeEach(async ({ page }) => { const lang = await getLang(page); @@ -262,18 +237,8 @@ test.describe("Generate JWT encoding examples", () => { const pickersUiDictionary = getPickersUiDictionary(lang); - const encoder = page.getByTestId(dataTestidDictionary.encoder.id); - - const exampleButton = encoder.getByRole("button", { - name: pickersUiDictionary.exampleAlgPicker.label, - }); - - await exampleButton.click(); - - await expect(exampleButton).not.toBeVisible(); - - const pickerIndicator = encoder.getByText( - pickersUiDictionary.exampleAlgPicker.defaultValue, + const pickerIndicator = page.getByText( + pickersUiDictionary.exampleAlgPicker.defaultValue ); await pickerIndicator.click(); @@ -311,7 +276,7 @@ test.describe("Generate JWT encoding examples", () => { } await expect(jwtOutput).toHaveValue( - /^[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*$/, + /^[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*$/ ); }); }); @@ -336,13 +301,13 @@ test.describe("encode JWTs", () => { const encoderWidget = page.getByTestId(dataTestidDictionary.encoder.id); const headerEditor = encoderWidget.getByTestId( - dataTestidDictionary.encoder.headerEditor.id, + dataTestidDictionary.encoder.headerEditor.id ); const payloadEditor = encoderWidget.getByTestId( - dataTestidDictionary.encoder.payloadEditor.id, + dataTestidDictionary.encoder.payloadEditor.id ); const secretKeyEditor = encoderWidget.getByTestId( - dataTestidDictionary.encoder.secretKeyEditor.id, + dataTestidDictionary.encoder.secretKeyEditor.id ); const jwtOutput = encoderWidget .getByTestId(dataTestidDictionary.encoder.jwt.id) @@ -415,16 +380,13 @@ test.describe("encode JWTs", () => { await expect(jwtOutput).toHaveValue(tokenWithUtf8Secret.jwt); - const formatPicker = secretKeyEditor.locator( - ".react-select__single-value", - ); - await formatPicker.click(); - - await page - .getByRole("option", { - name: tokenWithBase64urlSecret.secretEncoding, - }) - .click(); + const encodingSwitch = await page.getByTestId(dataTestidDictionary.encoder.switch) + expect(encodingSwitch).toBeVisible() + + if ( + tokenWithBase64urlSecret.secretEncoding === EncodingValues.BASE64URL + ) + await encodingSwitch.click(); await checkSecretKeyEncoderEditorStatusBarMessage({ page, @@ -479,12 +441,12 @@ test.describe("encode JWTs", () => { await expect(jwtOutput).toHaveValue(tokenWithPemKey.jwt); } else { await expect(jwtOutput).toHaveValue( - /^[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*$/, + /^[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*$/ ); } - const formatPicker = secretKeyEditor.locator( - ".react-select__single-value", + const formatPicker = page.locator( + ".react-select__single-value" ); await formatPicker.click(); @@ -501,7 +463,7 @@ test.describe("encode JWTs", () => { }); await secretKeyEditorInput.fill( - JSON.stringify(tokenWithJwkKey.privateKey, null, 2), + JSON.stringify(tokenWithJwkKey.privateKey, null, 2) ); await checkSecretKeyEncoderEditorStatusBarMessage({ @@ -514,7 +476,7 @@ test.describe("encode JWTs", () => { await expect(jwtOutput).toHaveValue(tokenWithJwkKey.jwt); } else { await expect(jwtOutput).toHaveValue( - /^[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*$/, + /^[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*$/ ); } diff --git a/next.config.mjs b/next.config.mjs index f4672921..8421cd46 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -3,18 +3,26 @@ import createMDX from "@next/mdx"; /** @type {import('next').NextConfig} */ const nextConfig = { webpack(config) { - config.module.rules.push({ - test: /\.svg$/, - use: { - loader: "@svgr/webpack", - options: { - svgoConfig: { - plugins: ["prefixIds"], + config.module.rules.push( + { + test: /\.svg$/, + resourceQuery: { not: /raw/ }, + use: { + loader: "@svgr/webpack", + options: { + svgoConfig: { + plugins: ["prefixIds"], + }, + ref: true, }, - ref: true, }, }, - }); + { + test: /\.svg$/i, + resourceQuery: /raw/, // Only apply this rule if '?raw' is present + type: "asset/source", + } + ); return config; }, diff --git a/public/images/ebook-image.png b/public/images/ebook-image.png deleted file mode 100644 index c24a572a..00000000 Binary files a/public/images/ebook-image.png and /dev/null differ diff --git a/public/images/ebook.svg b/public/images/ebook.svg new file mode 100644 index 00000000..05ada022 --- /dev/null +++ b/public/images/ebook.svg @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/en.dark.auth0-flow.png b/public/images/en.dark.auth0-flow.png deleted file mode 100644 index 5c7ae1ad..00000000 Binary files a/public/images/en.dark.auth0-flow.png and /dev/null differ diff --git a/public/images/en.diagram.png b/public/images/en.diagram.png new file mode 100644 index 00000000..5ed13e24 Binary files /dev/null and b/public/images/en.diagram.png differ diff --git a/public/images/en.light.auth0-flow.png b/public/images/en.light.auth0-flow.png deleted file mode 100644 index 0e07d32c..00000000 Binary files a/public/images/en.light.auth0-flow.png and /dev/null differ diff --git a/public/images/ja.dark.auth0-flow.png b/public/images/ja.dark.auth0-flow.png deleted file mode 100644 index 3faed616..00000000 Binary files a/public/images/ja.dark.auth0-flow.png and /dev/null differ diff --git a/public/images/ja.diagram.png b/public/images/ja.diagram.png new file mode 100644 index 00000000..5815c1e2 Binary files /dev/null and b/public/images/ja.diagram.png differ diff --git a/public/images/ja.light.auth0-flow.png b/public/images/ja.light.auth0-flow.png deleted file mode 100644 index 39b6d112..00000000 Binary files a/public/images/ja.light.auth0-flow.png and /dev/null differ diff --git a/public/images/mobile.dark.auth0-flow-bg.svg b/public/images/mobile.dark.auth0-flow-bg.svg deleted file mode 100644 index 8f861605..00000000 --- a/public/images/mobile.dark.auth0-flow-bg.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/mobile.en.dark.auth0-flow.svg b/public/images/mobile.en.dark.auth0-flow.svg deleted file mode 100644 index bed98685..00000000 --- a/public/images/mobile.en.dark.auth0-flow.svg +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/mobile.en.light.auth0-flow.svg b/public/images/mobile.en.light.auth0-flow.svg deleted file mode 100644 index 9a15f08e..00000000 --- a/public/images/mobile.en.light.auth0-flow.svg +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/mobile.ja.dark.auth0-flow.svg b/public/images/mobile.ja.dark.auth0-flow.svg deleted file mode 100644 index 88adfb6e..00000000 --- a/public/images/mobile.ja.dark.auth0-flow.svg +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/mobile.ja.light.auth0-flow.svg b/public/images/mobile.ja.light.auth0-flow.svg deleted file mode 100644 index 9f9f0d12..00000000 --- a/public/images/mobile.ja.light.auth0-flow.svg +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/mobile.light.auth0-flow-bg.svg b/public/images/mobile.light.auth0-flow-bg.svg deleted file mode 100644 index 2701f5dd..00000000 --- a/public/images/mobile.light.auth0-flow-bg.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/tablet.dark.auth0-flow-bg.svg b/public/images/tablet.dark.auth0-flow-bg.svg deleted file mode 100644 index 86b3ae54..00000000 --- a/public/images/tablet.dark.auth0-flow-bg.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/tablet.en.dark.auth0-flow.svg b/public/images/tablet.en.dark.auth0-flow.svg deleted file mode 100644 index 03d06c6d..00000000 --- a/public/images/tablet.en.dark.auth0-flow.svg +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/tablet.en.light.auth0-flow.svg b/public/images/tablet.en.light.auth0-flow.svg deleted file mode 100644 index c0854376..00000000 --- a/public/images/tablet.en.light.auth0-flow.svg +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/tablet.ja.dark.auth0-flow.svg b/public/images/tablet.ja.dark.auth0-flow.svg deleted file mode 100644 index fc01b4c1..00000000 --- a/public/images/tablet.ja.dark.auth0-flow.svg +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/tablet.ja.light.auth0-flow.svg b/public/images/tablet.ja.light.auth0-flow.svg deleted file mode 100644 index 5c9855e2..00000000 --- a/public/images/tablet.ja.light.auth0-flow.svg +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/tablet.light.auth0-flow-bg.svg b/public/images/tablet.light.auth0-flow-bg.svg deleted file mode 100644 index 9e875a45..00000000 --- a/public/images/tablet.light.auth0-flow-bg.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/img/jwt-logo.svg b/public/img/jwt-logo.svg new file mode 100644 index 00000000..54d020e9 --- /dev/null +++ b/public/img/jwt-logo.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/img/jwt-symbol.svg b/public/img/jwt-symbol.svg new file mode 100644 index 00000000..63da72ae --- /dev/null +++ b/public/img/jwt-symbol.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/img/jwt-wordmark.svg b/public/img/jwt-wordmark.svg new file mode 100644 index 00000000..3a2ecbd0 --- /dev/null +++ b/public/img/jwt-wordmark.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/app/[language]/page.tsx b/src/app/[language]/page.tsx index 4793d8e6..ef6a1637 100644 --- a/src/app/[language]/page.tsx +++ b/src/app/[language]/page.tsx @@ -16,12 +16,6 @@ import { DECODED_PAYLOAD_DESCRIPTION_KEY, DECODED_PAYLOAD_FORMAT_KEY, } from "@/features/decoder/config/decoder.config"; -import { - JWT_INFO_STATE_KEY, - JWT_WARNING_STATE_KEY, -} from "@/features/home/config/home.config"; -import { HeroModalStateValues } from "@/features/home/values/hero-modal-state.values"; -import { getSanitizedHeroModalStateValue } from "@/features/common/services/hero.utils"; import { getSanitizedDescriptionVisibilityValue } from "@/features/common/services/decoder.utils"; import { ClaimDescriptionVisibilityValues } from "@/features/common/values/claim-description-visibility.values"; import { getSanitizedDebuggerModeValues } from "@/features/common/services/debugger.utils"; @@ -60,15 +54,6 @@ export default function Home({ cookies().get(DECODED_PAYLOAD_DESCRIPTION_KEY)?.value || null, ) || ClaimDescriptionVisibilityValues.VISIBLE; - const jwtInfoState = - getSanitizedHeroModalStateValue( - cookies().get(JWT_INFO_STATE_KEY)?.value || null, - ) || HeroModalStateValues.OPEN; - const jwtWarningState = - getSanitizedHeroModalStateValue( - cookies().get(JWT_WARNING_STATE_KEY)?.value || null, - ) || HeroModalStateValues.OPEN; - const debuggerInitialMode: DebuggerModeValues = getSanitizedDebuggerModeValues( cookies().get(DEBUGGER_MODE_KEY)?.value || null, @@ -81,8 +66,6 @@ export default function Home({ decodedPayloadInitialTabId={decodedPayloadInitialTabId} decodedHeaderDescriptionVisibility={decodedHeaderDescriptionVisibility} decodedPayloadDescriptionVisibility={decodedPayloadDescriptionVisibility} - jwtInfoState={jwtInfoState} - jwtWarningState={jwtWarningState} // debuggerInitialMode={debuggerInitialMode} /> ); diff --git a/src/app/apple-icon.tsx b/src/app/apple-icon.tsx index f594f156..85294345 100644 --- a/src/app/apple-icon.tsx +++ b/src/app/apple-icon.tsx @@ -86,62 +86,62 @@ export default function Icon({ id }: { id: string }) { xmlns="http://www.w3.org/2000/svg" > diff --git a/src/app/global-error.tsx b/src/app/global-error.tsx index 911ceda8..6977c3da 100644 --- a/src/app/global-error.tsx +++ b/src/app/global-error.tsx @@ -22,7 +22,6 @@ export default function GlobalError({ diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx index 2cdb2033..84c939b1 100644 --- a/src/app/not-found.tsx +++ b/src/app/not-found.tsx @@ -15,7 +15,6 @@ export default function NotFound() { diff --git a/src/features/common/assets/arrow-head-icon.component.tsx b/src/features/common/assets/arrow-head-icon.component.tsx new file mode 100644 index 00000000..0910b453 --- /dev/null +++ b/src/features/common/assets/arrow-head-icon.component.tsx @@ -0,0 +1,21 @@ +import React from "react"; + +export const ArrowHeadIconComponent: React.FC = () => { + return ( + + + + ); +}; diff --git a/src/features/common/assets/auth0-logo.component.tsx b/src/features/common/assets/auth0-logo.component.tsx new file mode 100644 index 00000000..22f18ce7 --- /dev/null +++ b/src/features/common/assets/auth0-logo.component.tsx @@ -0,0 +1,53 @@ +interface Auth0LogoComponentProps { + title: string; +} + +export const Auth0LogoComponent: React.FC = ({title}) => { + return ( + + {title} + + + + + + + + + + + ); +}; diff --git a/src/features/common/assets/auth0-logo.png b/src/features/common/assets/auth0-logo.png deleted file mode 100644 index 51ff6735..00000000 Binary files a/src/features/common/assets/auth0-logo.png and /dev/null differ diff --git a/src/features/common/assets/download-icon.component.tsx b/src/features/common/assets/download-icon.component.tsx index c2a92fda..f401a279 100644 --- a/src/features/common/assets/download-icon.component.tsx +++ b/src/features/common/assets/download-icon.component.tsx @@ -3,33 +3,26 @@ import React from "react"; export const DownloadIconComponent: React.FC = () => { return ( + > - + > ); }; diff --git a/src/features/common/assets/jwt-flower.png b/src/features/common/assets/jwt-flower.png deleted file mode 100644 index 96b2e512..00000000 Binary files a/src/features/common/assets/jwt-flower.png and /dev/null differ 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..540dc371 --- /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..1a903280 --- /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/article/article.module.scss b/src/features/common/components/article/article.module.scss index 97f5a8cf..1f5ef8a4 100644 --- a/src/features/common/components/article/article.module.scss +++ b/src/features/common/components/article/article.module.scss @@ -7,26 +7,13 @@ .content { @include InnerContentGrid; - - padding: 3rem 0 3rem 0; - gap: 2rem; - - @media #{$breakpoint-dimension-sm} { - padding: 3rem 0 4rem 0; - } - - @media #{$breakpoint-dimension-md} { - gap: 3rem; - padding: 4rem 0 4rem 0; - } } .article { display: flex; flex-direction: column; - grid-column: span 6; - max-width: 48.5rem; + max-width: 56rem; justify-self: center; @media #{$breakpoint-dimension-sm} { @@ -47,7 +34,7 @@ font-size: 2rem; line-height: 2.5rem; font-style: normal; - font-weight: 400; + font-weight: 500; letter-spacing: -0.05rem; margin-bottom: 1.5rem; font-family: var(--font-secondary), sans-serif; @@ -68,13 +55,12 @@ h3 { color: var(--color_fg_bold); - font-size: 1.5rem; line-height: 2rem; font-style: normal; - font-weight: 400; + font-weight: 500; letter-spacing: -0.05rem; - margin-bottom: 2rem; + margin-bottom: 1.5rem; font-family: var(--font-secondary), sans-serif; :global(html[lang="ja"]) & { @@ -89,11 +75,11 @@ "clig" off, "liga" off; font-size: 1rem; - line-height: 1.5em; + line-height: 1.45; font-style: normal; font-weight: 400; letter-spacing: -0.005rem; - margin-bottom: 2rem; + margin-bottom: 1rem; :global(html[lang="ja"]) & { @include LocaleLineHeight("ja", 1.5rem); @@ -107,12 +93,10 @@ ul, ol { - list-style-position: inside; margin-top: 0; ul, ol { - list-style-position: inside; margin-left: 1.5rem; padding-left: 0; } @@ -121,11 +105,11 @@ li { margin-bottom: 0.75rem; font-size: 1rem; - line-height: 2rem; + line-height: 1.45; color: var(--color_fg_default); letter-spacing: -0.01px; - text-indent: -1.5rem; - padding-left: 1.5rem; + margin-left: 1rem; + padding-left: 1rem; :global(html[lang="ja"]) & { @include LocaleLineHeight("ja", 2rem); @@ -136,9 +120,9 @@ &, &:hover, &:active { - color: var(--color_fg_link_primary); text-decoration: underline; - font-weight: 400; + font-weight: 500; + text-underline-offset: 0.25rem; } } @@ -148,42 +132,70 @@ } pre { - margin-bottom: 2rem; - background-color: var(--color_bg_code-editor); + margin-bottom: 1rem; + background-color: var(--color_bg_layer_alternate); color: var(--color_fg_default); font-size: 0.875rem; line-height: 1.375rem; font-style: normal; font-weight: 400; letter-spacing: 0.15px; - padding: 0.75rem; + padding: 0.25rem; border: 1px solid var(--color_border_bold); - border-radius: 0.25rem; + border-radius: 1rem; code { - color: var(--color_code_dark_blue2); + display: block; + width: 100%; + color: var(--color_code_gray); background-color: var(--color_bg_code-editor); font-weight: 500; - } - - @media #{$breakpoint-dimension-sm} { - border-radius: 0.5rem; - padding: 1rem; + font-size: 0.875rem; + line-height: 1.35; + border-radius: 0.75rem; + padding: 0.75rem; + box-shadow: + 0 0 0 0.5px rgba(0, 0, 0, 0.12), + 0 2px 2px -1px rgba(0, 0, 0, 0.04), + 0 4px 4px -2px rgba(0, 0, 0, 0.04), + 0 6px 6px -3px rgba(0, 0, 0, 0.04); } } blockquote { - background-color: var(--color_bg_state_info_subtle); - padding: 1rem 1.5rem; - margin: 2rem 0 2rem 0; - border-radius: 0 0.5rem 0.5rem 0; - border-left: 4px solid var(--color_border_state_info); + background-color: var(--color_bg_layer); + font-size: 0.875rem; + padding: 1rem; + margin: 2rem 0; + border-radius: 1rem; + border: 1px solid var(--color_border_default); + box-shadow: + 0 0 0 0.5px rgba(0, 0, 0, 0.12), + 0 2px 2px -1px rgba(0, 0, 0, 0.04), + 0 4px 4px -2px rgba(0, 0, 0, 0.04), + 0 6px 6px -3px rgba(0, 0, 0, 0.04); & * { - color: var(--color_fg_on_state_info_subtle); + color: var(--color_fg_default); padding: 0; margin: 0; } + + p { + display: flex; + flex-direction: row; + font-size: 0.875rem; + align-items: center; + gap: 0.75rem; + } + + ::before { + content: url("data:image/svg+xml,%3Csvg width='12' height='12' viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6 0.5C9.03757 0.5 11.5 2.96243 11.5 6C11.5 9.03757 9.03757 11.5 6 11.5C2.96243 11.5 0.5 9.03757 0.5 6C0.5 2.96243 2.96243 0.5 6 0.5ZM6 5.25C5.72386 5.25 5.5 5.47386 5.5 5.75V8.75H6.5V5.75C6.5 5.47386 6.27614 5.25 6 5.25ZM6 3.125C5.65482 3.125 5.375 3.40482 5.375 3.75C5.375 4.09518 5.65482 4.375 6 4.375C6.34518 4.375 6.625 4.09518 6.625 3.75C6.625 3.40482 6.34518 3.125 6 3.125Z' fill='%235C5650'/%3E%3C/svg%3E%0A"); + display: block; + padding: 0.25rem 0.4rem 0.125rem; + border-radius: 9999px; + background-color: var(--color_bg_layer_alternate); + } } *:has(+ blockquote) { diff --git a/src/features/common/components/auth0-cta/auth0-cta.component.tsx b/src/features/common/components/auth0-cta/auth0-cta.component.tsx index 2bbb4bca..28248ce7 100644 --- a/src/features/common/components/auth0-cta/auth0-cta.component.tsx +++ b/src/features/common/components/auth0-cta/auth0-cta.component.tsx @@ -2,10 +2,9 @@ import React from "react"; import { getLocalizedSecondaryFont } from "@/libs/theme/fonts"; import clsx from "clsx"; import { Auth0DictionaryModel } from "@/features/localization/models/auth0-dictionary.model"; -import { BoxComponent } from "@/features/common/components/box/box.component"; import styles from "./auth0-cta.module.scss"; import Link from "next/link"; -import NextArrow from "@/features/common/assets/arrow-right.svg"; +import { ArrowHeadIconComponent } from "../../assets/arrow-head-icon.component"; interface Auth0CtaComponentProps { languageCode: string; @@ -17,40 +16,36 @@ export const Auth0CtaComponent: React.FC = ({ dictionary, }) => { return ( - -
-
-
-

- {dictionary.title} -

-

{dictionary.description}

-
- {dictionary.ctaButton && ( - - {dictionary.ctaButton.label} -
- +
+
+
+
+
+
+

+ {dictionary.title} +

+

+ {dictionary.description} +

- - )} + + {dictionary.ctaButton.label} + + +
+
+
-
- +
); }; diff --git a/src/features/common/components/auth0-cta/auth0-cta.module.scss b/src/features/common/components/auth0-cta/auth0-cta.module.scss index 8f79f1ef..de8a0cf0 100644 --- a/src/features/common/components/auth0-cta/auth0-cta.module.scss +++ b/src/features/common/components/auth0-cta/auth0-cta.module.scss @@ -1,11 +1,22 @@ @use "@/libs/theme/styles/variables" as *; @use "@/libs/theme/styles/mixins" as *; -.container { - @include Container; +.cta_container { position: relative; overflow: hidden; background: var(--color_bg_auth0-cta); + grid-column: span 8; + border-radius: 1rem; + max-width: 1008px; + border: 1px solid var(--color_border_default); + box-shadow: 0 2px 12px -4px rgba(0,0,0,.08),0 2px 1px -.5px rgba(0,0,0,.04),0 2px 4px -4px rgba(0,0,0,.05),inset -.5px -.5px 2px rgba(0,0,0,.04); + + @media #{$breakpoint-dimension-md} { + margin: 0 auto; + &:before { + background: none; + } + } &:before { position: absolute; @@ -15,83 +26,20 @@ width: 100%; height: 100%; } - - :global(html[data-theme="light"]) &:before { - background-image: url("/images/mobile.light.auth0-flow-bg.svg"); - background-size: cover; - } - :global(html[data-theme="system-light"]) &:before { - background-image: url("/images/mobile.light.auth0-flow-bg.svg"); - background-size: cover; - } - :global(html[data-theme="dark"]) &:before { - background-image: url("/images/mobile.dark.auth0-flow-bg.svg"); - background-size: cover; - } - :global(html[data-theme="system-dark"]) &:before { - background-image: url("/images/mobile.dark.auth0-flow-bg.svg"); - background-size: cover; - } - - @media #{$breakpoint-dimension-sm} { - :global(html[data-theme="light"]) &:before { - background-image: url("/images/tablet.light.auth0-flow-bg.svg"); - background-size: cover; - } - :global(html[data-theme="system-light"]) &:before { - background-image: url("/images/tablet.light.auth0-flow-bg.svg"); - background-size: cover; - } - :global(html[data-theme="dark"]) &:before { - background-image: url("/images/tablet.dark.auth0-flow-bg.svg"); - background-size: cover; - } - :global(html[data-theme="system-dark"]) &:before { - background-image: url("/images/tablet.dark.auth0-flow-bg.svg"); - background-size: cover; - } - } - - @media #{$breakpoint-dimension-md} { - padding-bottom: 7.5rem; - background: unset; - - &:before { - background: none; - } - - :global(html[data-theme="light"]) &:before { - background-image: none; - } - :global(html[data-theme="system-light"]) &:before { - background-image: none; - } - :global(html[data-theme="dark"]) &:before { - background-image: none; - } - :global(html[data-theme="system-dark"]) &:before { - background-image: none; - } - - &:after { - display: none; - background: none; - } - } } -.wrapper { - @include ExtendedGrid; +.cta_wrapper { + max-width: 1536px; + margin: 0 auto; position: relative; justify-content: center; - - border-radius: 0; + border-radius: 1rem; overflow: hidden; + width: 100%; + height: 100%; @media #{$breakpoint-dimension-md} { background: var(--color_bg_auth0-cta); - border-radius: 1.5rem; - &:before { position: absolute; bottom: 0; @@ -100,83 +48,63 @@ width: 100%; height: 100%; } - - :global(html[data-theme="light"]) &:before { - background: url("/images/light.auth0-flow-bg.svg") no-repeat right - bottom; - background-size: cover; - } - :global(html[data-theme="system-light"]) &:before { - background: url("/images/light.auth0-flow-bg.svg") no-repeat right - bottom; - background-size: cover; - } - :global(html[data-theme="dark"]) &:before { - background: url("/images/dark.auth0-flow-bg.svg") no-repeat right - bottom; - background-size: cover; - } - :global(html[data-theme="system-dark"]) &:before { - background: url("/images/dark.auth0-flow-bg.svg") no-repeat right - bottom; - background-size: cover; - } } } -.content { - @include InnerContentFlex; +.cta_content { + display: flex; justify-content: center; + width: 100%; + height: 100%; - @media #{$breakpoint-dimension-sm} { - @include InnerContentGrid; + @media #{$breakpoint-dimension-md} { + display: grid; + grid-template-columns: repeat(12,minmax(0,1fr)); + width: 100%; } } -.cta { +.cta_cta { z-index: 1; width: 100%; display: flex; flex-direction: column; - padding: 2.5rem 0; + padding: .25rem; align-items: center; gap: 2rem; - @media #{$breakpoint-dimension-sm} { - padding: 3rem 0; - grid-column: 1 / -1; + @media #{$breakpoint-dimension-md} { + grid-column: 1/-1; display: grid; grid-template-columns: subgrid; } - - @media #{$breakpoint-dimension-md} { - padding: 4rem 0; - } } .cta__copy { - grid-column: 1 / -1; + grid-column: 1/-1; display: flex; flex-direction: column; gap: 1.5rem; - align-items: center; - - @media #{$breakpoint-dimension-sm} { - align-items: unset; - } + align-items: flex-start; + padding: 2.5rem; @media #{$breakpoint-dimension-md} { - align-items: center; - flex-direction: row; + grid-column: span 6; + align-items: flex-start; + flex-direction: column; gap: 2.5rem; } + + @media #{$breakpoint-dimension-sm} { + align-items: unset; + } } .cta__text { width: 100%; display: flex; flex-direction: column; - gap: 0.75rem; + gap: .75rem; @media #{$breakpoint-dimension-sm} { gap: 1rem; @@ -187,26 +115,15 @@ width: 100%; margin: 0; text-align: left; - font-size: 2.25rem; - line-height: 2.75rem; - letter-spacing: -0.03rem; - font-weight: 400; - color: var(--color_fg_bold); + font-size: 1.5rem; + line-height: 1.15; + color: #fff; + font-feature-settings: lining-nums proportional-nums; font-variant-numeric: lining-nums proportional-nums; font-style: normal; - :global(html[lang="ja"]) & { - @include LocaleLineHeight("ja", 2.25rem); - } - - @media #{$breakpoint-dimension-sm} { - font-size: 2.5rem; - line-height: 3rem; - letter-spacing: -0.08rem; - - :global(html[lang="ja"]) & { - @include LocaleLineHeight("ja", 2.5rem); - } + @media #{$breakpoint-dimension-lg} { + font-size: 2.5rem; } } @@ -216,167 +133,42 @@ line-height: 1.5rem; font-weight: 400; - :global(html[lang="ja"]) & { - @include LocaleLineHeight("ja", 1rem); - } - @media #{$breakpoint-dimension-sm} { max-width: 40rem; - font-size: 1.25rem; - line-height: 1.75rem; - letter-spacing: 0.01rem; - - :global(html[lang="ja"]) & { - @include LocaleLineHeight("ja", 1.25rem); - } - } - - @media #{$breakpoint-dimension-md} { - font-size: 1.25rem; - line-height: 1.75rem; - letter-spacing: 0.1px; - - :global(html[lang="ja"]) & { - @include LocaleLineHeight("ja", 1.25rem); - } - } -} - -.cta__media { - position: relative; - grid-column: 1 / -1; - align-self: center; - justify-self: center; - display: flex; - align-items: center; - justify-content: center; - gap: 2.4rem; - - overflow: hidden; - - width: 100%; - - background-size: 100%; - - aspect-ratio: 281 / 547; - - :global(html[lang="en"][data-theme="light"]) & { - background-image: url("/images/mobile.en.light.auth0-flow.svg"); - } - :global(html[lang="en"][data-theme="dark"]) & { - background-image: url("/images/mobile.en.dark.auth0-flow.svg"); - } - :global(html[lang="ja"][data-theme="light"]) & { - background-image: url("/images/mobile.ja.light.auth0-flow.svg"); - } - :global(html[lang="ja"][data-theme="dark"]) & { - background-image: url("/images/mobile.ja.dark.auth0-flow.svg"); - } - - :global(html[lang="en"][data-theme="system-light"]) & { - background-image: url("/images/mobile.en.light.auth0-flow.svg"); - } - :global(html[lang="en"][data-theme="system-dark"]) & { - background-image: url("/images/mobile.en.dark.auth0-flow.svg"); - } - :global(html[lang="ja"][data-theme="system-light"]) & { - background-image: url("/images/mobile.ja.light.auth0-flow.svg"); - } - :global(html[lang="ja"][data-theme="system-dark"]) & { - background-image: url("/images/mobile.ja.dark.auth0-flow.svg"); - } - - @media #{$breakpoint-dimension-sm} { - aspect-ratio: 672 / 251; - - :global(html[lang="en"][data-theme="light"]) & { - background-image: url("/images/tablet.en.light.auth0-flow.svg"); - } - :global(html[lang="en"][data-theme="dark"]) & { - background-image: url("/images/tablet.en.dark.auth0-flow.svg"); - } - :global(html[lang="ja"][data-theme="light"]) & { - background-image: url("/images/tablet.ja.light.auth0-flow.svg"); - } - :global(html[lang="ja"][data-theme="dark"]) & { - background-image: url("/images/tablet.ja.dark.auth0-flow.svg"); - } - - :global(html[lang="en"][data-theme="system-light"]) & { - background-image: url("/images/tablet.en.light.auth0-flow.svg"); - } - :global(html[lang="en"][data-theme="system-dark"]) & { - background-image: url("/images/tablet.en.dark.auth0-flow.svg"); - } - :global(html[lang="ja"][data-theme="system-light"]) & { - background-image: url("/images/tablet.ja.light.auth0-flow.svg"); - } - :global(html[lang="ja"][data-theme="system-dark"]) & { - background-image: url("/images/tablet.ja.dark.auth0-flow.svg"); - } + font-size: 1rem; + line-height: 1.5rem; + letter-spacing: -.01rem; } @media #{$breakpoint-dimension-md} { - aspect-ratio: 3216 / 861; - - :global(html[lang="en"][data-theme="light"]) & { - background-image: url("/images/en.light.auth0-flow.png"); - } - :global(html[lang="en"][data-theme="dark"]) & { - background-image: url("/images/en.dark.auth0-flow.png"); - } - :global(html[lang="ja"][data-theme="light"]) & { - background-image: url("/images/ja.light.auth0-flow.png"); - } - :global(html[lang="ja"][data-theme="dark"]) & { - background-image: url("/images/ja.dark.auth0-flow.png"); - } - - :global(html[lang="en"][data-theme="system-light"]) & { - background-image: url("/images/en.light.auth0-flow.png"); - } - :global(html[lang="en"][data-theme="system-dark"]) & { - background-image: url("/images/en.dark.auth0-flow.png"); - } - :global(html[lang="ja"][data-theme="system-light"]) & { - background-image: url("/images/ja.light.auth0-flow.png"); - } - :global(html[lang="ja"][data-theme="system-dark"]) & { - background-image: url("/images/ja.dark.auth0-flow.png"); - } + font-size: 1rem; + line-height: 1.5rem; + letter-spacing: -.1px; } } -.button { +.cta__button { display: flex; - padding: 1rem 2rem; + padding: .25rem .75rem; justify-content: center; align-items: center; - gap: 0.5rem; - flex-shrink: 0; - - border: 1px solid var(--color_border_button_outline); - border-radius: 0.375rem; - background: transparent; - backdrop-filter: blur(1.5rem); - - color: var(--color_fg_on_button_outline); - font-size: 1rem; - line-height: 1.5rem; - font-style: normal; + gap: .5rem; + border-radius: 9999px; + background: var(--color_bg_layer); + border: 1px solid var(--color_border_default); + box-shadow: 0 1px 1px -.5px rgba(0,0,0,.04),0 3px 3px -1.5px rgba(0,0,0,.04),0 6px 6px -3px rgba(0,0,0,.04),0 12px 12px -6px rgba(0,0,0,.04),0 24px 24px -12px rgba(0,0,0,.04),inset 0 0 0 1px rgba(0,0,0,.08); + color: var(--color_fg_default); + font-size: .875rem; + font-weight: 700; + line-height: 1.375rem; font-weight: 500; - letter-spacing: 0.2px; - - width: 100%; + letter-spacing: -.2px; + transition: all .3s ease-in-out; - :global(html[lang="ja"]) & { - @include LocaleLineHeight("ja", 1rem); - } - - &:focus-visible { - outline: solid 1px var(--color_border_focus); - outline-offset: 0.125rem; - border-radius: 0.125rem; + &:hover { + border: 1px solid var(--color_border_bold); + box-shadow: 0 0 0 4px var(--color_button_focus_inverse); + transition: all .2s ease-out; } @media #{$breakpoint-dimension-sm} { @@ -384,34 +176,41 @@ } } -.button__arrow { - flex-shrink: 0; +.cta__media { + position: relative; + grid-column: 1/-1; + align-self: center; + justify-self: center; display: flex; align-items: center; justify-content: center; - height: 0.75rem; - width: 0.75rem; - - svg { - height: 0.75rem; - width: 0.75rem; + gap: 2.4rem; + min-height: 348px; + overflow: hidden; + width: 100%; + height: 100%; + border-radius: .75rem; + background-size: cover; + background-position: 50%; + background-repeat: no-repeat; - path { - fill: var(--color_fg_on_button_outline); - } + :global(html[lang="en"]) & { + background-image: url("/images/en.diagram.png"); } - @media #{$breakpoint-dimension-sm} { - height: 1rem; - width: 1rem; + :global(html[lang="ja"]) & { + background-image: url("/images/ja.diagram.png"); + } - svg { - height: 0.875rem; - width: 0.875rem; - } + @media #{$breakpoint-dimension-md} { + border-bottom-left-radius: 12rem; + grid-column: span 6; + align-items: flex-start; + flex-direction: column; + gap: 2.5rem; } - &[data-rotation="true"] { - animation: rotate 2s linear infinite; + @media #{$breakpoint-dimension-sm} { + align-items: unset; } -} +} \ No newline at end of file diff --git a/src/features/common/components/bars/ribbon/assets/dark-icon.component.tsx b/src/features/common/components/bars/ribbon/assets/dark-icon.component.tsx index 1f7d4251..3961a806 100644 --- a/src/features/common/components/bars/ribbon/assets/dark-icon.component.tsx +++ b/src/features/common/components/bars/ribbon/assets/dark-icon.component.tsx @@ -3,20 +3,18 @@ import React from "react"; export const DarkIconComponent: React.FC = () => { return ( - + ); }; diff --git a/src/features/common/components/bars/ribbon/assets/globe-icon.component.tsx b/src/features/common/components/bars/ribbon/assets/globe-icon.component.tsx index 7552b6c2..71ee0194 100644 --- a/src/features/common/components/bars/ribbon/assets/globe-icon.component.tsx +++ b/src/features/common/components/bars/ribbon/assets/globe-icon.component.tsx @@ -3,10 +3,11 @@ import React from "react"; export const GlobeIconComponent: React.FC = () => { return (
diff --git a/src/features/common/components/card-message/card-message.module.scss b/src/features/common/components/card-message/card-message.module.scss index a4a46757..c96542cd 100644 --- a/src/features/common/components/card-message/card-message.module.scss +++ b/src/features/common/components/card-message/card-message.module.scss @@ -4,7 +4,7 @@ .span, .p, .code { - font-size: 0.75rem; + font-size: .875rem; line-height: 1.125rem; } diff --git a/src/features/common/components/card-tabs/card-tabs.component.tsx b/src/features/common/components/card-tabs/card-tabs.component.tsx index 00af36ee..435b1b16 100644 --- a/src/features/common/components/card-tabs/card-tabs.component.tsx +++ b/src/features/common/components/card-tabs/card-tabs.component.tsx @@ -129,16 +129,20 @@ const CardTabs: React.FC = ({ {compactTitle} + {activeCard.slots?.toolbar && ( +
+
+ {activeCard.slots.toolbar} +
+
+ )} ); })} - {activeCard.slots?.toolbar && ( -
- {activeCard.slots.toolbar} -
- )} {cards.map((card) => { return ( @@ -181,7 +185,7 @@ const CardTabs: React.FC = ({ onSelectionChange={(e) => { handleActiveCardChange(e.toString()); }} - className={clsx(MonoFont.className, styles.cardTabs)} + className={styles.cardTabs} > <> @@ -197,16 +201,18 @@ const CardTabs: React.FC = ({ {compactTitle} + {activeCard.slots?.toolbar && ( +
+
+ {activeCard.slots.toolbar} +
+
+ )} ); })}
- {activeCard.slots?.toolbar && ( -
- {activeCard.slots.toolbar} -
- )} {cards.map((card) => { return ( diff --git a/src/features/common/components/card-tabs/card-tabs.module.scss b/src/features/common/components/card-tabs/card-tabs.module.scss index 8e7a0c44..2db5a3b3 100644 --- a/src/features/common/components/card-tabs/card-tabs.module.scss +++ b/src/features/common/components/card-tabs/card-tabs.module.scss @@ -8,46 +8,42 @@ width: 100%; max-height: fit-content; flex-direction: column; - row-gap: 0.5rem; + row-gap: 1rem; cursor: default; } .cardTabs__title { - grid-column: 1 / -1; + grid-column: 1/-1; display: flex; align-items: center; - gap: 0.25rem; - text-transform: uppercase; + gap: .25rem; color: var(--color_fg_default); - font-size: 0.875rem; - line-height: 1.375rem; - font-weight: 500; - letter-spacing: 0.24px; - margin-right: 8px; + font-size: 1rem; + line-height: 1.15rem; + font-weight: 700; + letter-spacing: -.1px; } .cardTabs { - position: relative; - grid-column: span 6; - display: flex; - height: 100%; - width: 100%; - flex-direction: column; - border-radius: 0.5rem; - border: 1px solid var(--color_border_bold); - background: var(--color_bg_layer_alternate); - overflow: hidden; + grid-column: span 6; + display: flex; + flex-direction: column; + border-radius: 1rem; + border: .5px solid var(--color_border_default); + background: var(--color_bg_layer_alternate-bold); + letter-spacing: -.1px; + gap: .25rem; } $cardTabs__tabList__height: 2.5rem; .cardTabs__tabList { - position: relative; - display: flex; - border-bottom: 1px solid var(--color_border_bold); - height: $cardTabs__tabList__height; - flex-shrink: 0; + position: relative; + display: flex; + height: 2.5rem; + padding: .5rem; + gap: .25rem; } .cardTabs__title__container { @@ -56,23 +52,21 @@ $cardTabs__tabList__height: 2.5rem; } .cardTab__title { - position: relative; - display: flex; - align-items: center; - gap: 0.25rem; - text-transform: uppercase; - padding: 0.5rem 0.75rem; - border-bottom: 0.125rem solid transparent; - color: var(--color_fg_default); - font-size: 0.75rem; - line-height: 1.125rem; - font-weight: 500; - letter-spacing: 0.24px; - cursor: pointer; + display: flex; + align-items: center; + gap: .25rem; + border-radius: .5rem; + padding: 0 .5rem; + color: var(--color_fg_default); + font-size: .875rem; + line-height: 1.125rem; + font-weight: 500; + letter-spacing: -.1px; + cursor: pointer; &[data-selected="true"] { - color: var(--color_fg_selected); - border-bottom: 0.125rem solid var(--color_border_selected); + background-color: var(--color_bg_layer); + box-shadow: 0 0 0 1px rgba(0,0,0,.04),0 1px 1px -.5px rgba(0,0,0,.04),inset 0 0 0 1px rgba(0,0,0,.04); } &[data-focus-visible]:after { @@ -86,6 +80,7 @@ $cardTabs__tabList__height: 2.5rem; .cardTab__title__full { display: none; + font-weight: 500; @media #{$breakpoint-dimension-sm} { display: flex; @@ -99,14 +94,24 @@ $cardTabs__tabList__height: 2.5rem; } .cardTabs__actionButtons { - position: absolute; - right: 0.75rem; - top: 0; - display: flex; - align-items: center; - justify-content: center; - gap: 0.5rem; - height: $cardTabs__tabList__height; + position: absolute; + right: 0.75rem; + top: 50%; + transform: translateY(-50%); + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; +} + +.cardTabs__tooltipContainer { + position: relative; + display: inline-block; + &:hover { + .button__tooltip { + opacity: 1; + } + } } .cardTabs__body { diff --git a/src/features/common/components/card-toolbar-button/card-toolbar-button.component.tsx b/src/features/common/components/card-toolbar-button/card-toolbar-button.component.tsx index 212703a2..5742102d 100644 --- a/src/features/common/components/card-toolbar-button/card-toolbar-button.component.tsx +++ b/src/features/common/components/card-toolbar-button/card-toolbar-button.component.tsx @@ -7,12 +7,13 @@ import { MonoFont } from "@/libs/theme/fonts"; export interface CardToolbarButtonComponentProps extends AriaButtonOptions<"span"> { variant: "standard" | "icon"; + tooltipText?: string; } export const CardToolbarButtonComponent: React.FC< PropsWithChildren > = (props) => { - const { variant, children } = props; + const { variant, tooltipText, children } = props; const ref = useRef(null); @@ -22,20 +23,26 @@ export const CardToolbarButtonComponent: React.FC< elementType: "span", preventFocusOnPress: true, }, - ref, + ref ); return ( - + {tooltipText && ( + {tooltipText} )} - data-style="compact" - > - {children} - +
); }; diff --git a/src/features/common/components/card-toolbar-button/card-toolbar-button.module.scss b/src/features/common/components/card-toolbar-button/card-toolbar-button.module.scss index b4fb851e..eff9edaf 100644 --- a/src/features/common/components/card-toolbar-button/card-toolbar-button.module.scss +++ b/src/features/common/components/card-toolbar-button/card-toolbar-button.module.scss @@ -1,25 +1,44 @@ +.button__tooltipContainer { + position: relative; + display: inline-block; + &:hover { + .button__tooltip { + opacity: 1; + } + } +} .button__standard { - gap: 0.75rem; - border-radius: 0.25rem; - border: 1px solid var(--color_border_button); + gap: .75rem; + border-radius: .5rem; color: var(--color_fg_on_button_subtle); - font-variant-numeric: slashed-zero; - font-feature-settings: - "clig" off, - "liga" off; + z-index: 2; background-color: transparent; text-transform: uppercase; cursor: pointer; - letter-spacing: 0.2px; + padding: .75rem; + letter-spacing: .2px; appearance: none; display: flex; align-items: center; + justify-content: center; + border: 1px solid transparent; + transition: all .2s ease-out; + width: 2rem; + height: 2rem; + font-size: .875rem; + line-height: 1.375rem; &[aria-disabled="true"] { opacity: 0.48; cursor: not-allowed; } + &:hover { + background-color: var(--color_bg_layer); + border: 1px solid var(--color_border_default); + transition: all .2s ease-out; + } + padding: 0 0.75rem; width: 5rem; height: 2rem; @@ -27,11 +46,11 @@ line-height: 1.375rem; &[data-style="compact"] { - padding: 0.125rem 0.375rem; - width: unset; - height: unset; - font-size: 0.75rem; - line-height: 1.25rem; + padding: .25rem; + width: 2rem; + height: 2rem; + font-size: .8125rem; + line-height: 1.35; } svg { @@ -46,6 +65,25 @@ } } +.button__tooltip { + position: absolute; + top: -30px; + left: 50%; + transform: translateX(-50%); + padding: 4px 8px; + color: var(--color_fg_bold); + background-color: var(--color_bg_layer); + border: 1px solid var(--color_border_default); + border-radius: .5rem; + font-size: .75rem; + white-space: nowrap; + opacity: 0; + pointer-events: none; + transition: opacity .2s ease-in-out; + box-shadow: 0 2px 4px rgba(0,0,0,.1); + z-index: 1000; +} + .button__icon { gap: 0.75rem; border-radius: 0.25rem; @@ -72,10 +110,9 @@ } &:hover { - background-color: var(--color_bg_layer_alternate-bold); - svg { - stroke: var(--color_fg_selected); - } + background-color: var(--color_bg_layer); + border: 1px solid var(--color_border_default); + transition: all .2s ease-out; } &:focus-visible { diff --git a/src/features/common/components/card-toolbar-buttons/card-toolbar-clear-button/card-toolbar-clear-button.component.tsx b/src/features/common/components/card-toolbar-buttons/card-toolbar-clear-button/card-toolbar-clear-button.component.tsx index bb046555..2afb7879 100644 --- a/src/features/common/components/card-toolbar-buttons/card-toolbar-clear-button/card-toolbar-clear-button.component.tsx +++ b/src/features/common/components/card-toolbar-buttons/card-toolbar-clear-button/card-toolbar-clear-button.component.tsx @@ -4,6 +4,7 @@ import { CardToolbarButtonComponent, CardToolbarButtonComponentProps, } from "@/features/common/components/card-toolbar-button/card-toolbar-button.component"; +import { ClearIcon } from "../../icons/clear/clear-icon"; interface CardToolbarClearButtonComponentProps extends Omit { @@ -16,8 +17,12 @@ export const CardToolbarClearButtonComponent: React.FC< const dictionary = getButtonsUiDictionary(languageCode); return ( - - {dictionary.clearButton.label} + + ); }; diff --git a/src/features/common/components/card-toolbar-buttons/card-toolbar-copy-button/card-toolbar-copy-button.component.tsx b/src/features/common/components/card-toolbar-buttons/card-toolbar-copy-button/card-toolbar-copy-button.component.tsx index 788e411a..757c73b4 100644 --- a/src/features/common/components/card-toolbar-buttons/card-toolbar-copy-button/card-toolbar-copy-button.component.tsx +++ b/src/features/common/components/card-toolbar-buttons/card-toolbar-copy-button/card-toolbar-copy-button.component.tsx @@ -4,6 +4,8 @@ import { CardToolbarButtonComponentProps, } from "@/features/common/components/card-toolbar-button/card-toolbar-button.component"; import { getButtonsUiDictionary } from "@/features/localization/services/ui-language-dictionary.service"; +import { CopyIcon } from "../../icons/copy/copy-icon"; +import { CheckIcon } from "../../icons/check/check-icon"; interface CardToolbarCopyButtonComponentProps extends Omit, "variant"> { @@ -15,7 +17,6 @@ export const CardToolbarCopyButtonComponent: React.FC< CardToolbarCopyButtonComponentProps > = ({ languageCode, value, isDisabled, ...props }) => { const dictionary = getButtonsUiDictionary(languageCode); - const [isCopied, setIsCopied] = useState(false); const resetCopy = () => { @@ -36,10 +37,13 @@ export const CardToolbarCopyButtonComponent: React.FC< onPress={copyValue} onBlur={resetCopy} variant="standard" + tooltipText={ + isCopied + ? dictionary.copyButton.done.label + : dictionary.copyButton.idle.label + } > - {isCopied - ? dictionary.copyButton.done.label - : dictionary.copyButton.idle.label} + {isCopied ? : } ); }; diff --git a/src/features/common/components/card-toolbar-buttons/card-toolbar-description-button/card-toolbar-description-button.component.tsx b/src/features/common/components/card-toolbar-buttons/card-toolbar-description-button/card-toolbar-description-button.component.tsx index 0cc9e1ba..0304d2ca 100644 --- a/src/features/common/components/card-toolbar-buttons/card-toolbar-description-button/card-toolbar-description-button.component.tsx +++ b/src/features/common/components/card-toolbar-buttons/card-toolbar-description-button/card-toolbar-description-button.component.tsx @@ -11,6 +11,8 @@ import { CardToolbarButtonComponent, CardToolbarButtonComponentProps, } from "@/features/common/components/card-toolbar-button/card-toolbar-button.component"; +import { EyeOpenIcon } from "../../icons/eye-open/eye-open-icon"; +import { EyeCloseIcon } from "../../icons/eye-close/eye-close-icon"; interface CardToolbarDescriptionButtonComponentProps extends Omit { @@ -26,12 +28,12 @@ export const CardToolbarDescriptionButtonComponent: React.FC< const setDescriptionVisibility$ = useDebuggerStore((state) => outputType === "header" ? state.setDecodedHeaderDescriptionVisibility$ - : state.setDecodedPayloadDescriptionVisibility$, + : state.setDecodedPayloadDescriptionVisibility$ ); const descriptionVisibility$ = useDebuggerStore((state) => outputType === "header" ? state.decodedHeaderDescriptionVisibility$ - : state.decodedPayloadDescriptionVisibility$, + : state.decodedPayloadDescriptionVisibility$ ); const [descVisibility, setDescVisibility] = @@ -47,7 +49,7 @@ export const CardToolbarDescriptionButtonComponent: React.FC< ClaimDescriptionVisibilityValues.HIDDEN, { secure: true, - }, + } ); setDescriptionVisibility$(ClaimDescriptionVisibilityValues.HIDDEN); @@ -63,7 +65,7 @@ export const CardToolbarDescriptionButtonComponent: React.FC< ClaimDescriptionVisibilityValues.VISIBLE, { secure: true, - }, + } ); setDescriptionVisibility$(ClaimDescriptionVisibilityValues.VISIBLE); @@ -79,15 +81,22 @@ export const CardToolbarDescriptionButtonComponent: React.FC< - {descVisibility === ClaimDescriptionVisibilityValues.VISIBLE - ? dictionary.hideDetailsButton.label - : dictionary.showDetailsButton.label} + {descVisibility === ClaimDescriptionVisibilityValues.VISIBLE ? ( + + ) : ( + + )} ); }; diff --git a/src/features/common/components/card/card.component.tsx b/src/features/common/components/card/card.component.tsx index bb9e5912..39d92ed9 100644 --- a/src/features/common/components/card/card.component.tsx +++ b/src/features/common/components/card/card.component.tsx @@ -1,14 +1,21 @@ 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"; +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; languageCode: string; title: string; compactTitle: string; + hasHeaderIcon?: boolean; options: Partial<{ noPadding: boolean; fullWidth: boolean; @@ -44,6 +51,7 @@ export const CardComponent: React.FC = (props) => { }, messages, slots, + hasHeaderIcon = false, } = props; const cardId = useId(); @@ -61,12 +69,12 @@ export const CardComponent: React.FC = (props) => { aria-label={canRenderTitle ? undefined : title} heap-ignore="true" className={clsx( - MonoFont.className, styles.card, options && options.fullWidth && styles.card__hasFullWidth, - options && options.fullHeight && styles.card__hasFullHeight, + options && options.fullHeight && styles.card__hasFullHeight )} data-type={options && options.isOutput ? "output" : "input"} + data-no-padding={options ? options.noPadding : undefined} data-frameless={options && options.frameless} > {!options?.hideTitle && ( @@ -74,21 +82,9 @@ export const CardComponent: React.FC = (props) => { {titleKey && (
{titleKey && !compactTitle && ( -

- - {titleKey} - {titleValue && `: `} - - {titleValue && ( - - {titleValue} - - )} -

- )} - {titleKey && compactTitle && ( - <> -

+
+ {hasHeaderIcon && } +

{titleKey} {titleValue && `: `} @@ -99,6 +95,24 @@ export const CardComponent: React.FC = (props) => { )}

+
+ )} + {titleKey && compactTitle && ( + <> +
+ {hasHeaderIcon && } +

+ + {titleKey} + {titleValue && `: `} + + {titleValue && ( + + {titleValue} + + )} +

+

{compactTitle} @@ -111,6 +125,16 @@ export const CardComponent: React.FC = (props) => { )} )} +
+
+ {children} +
+
{messages && messages.errors && messages.errors.length > 0 ? (
= (props) => { > {messages.success.map((line, index) => { return ( - {line} +
+ + {line} +
); })}
) : null} + {slots?.notification} {messages && messages.warnings && messages.warnings.length > 0 && (
= (props) => { })}
)} - {slots?.notification} -
- {children} -
{slots?.footer && (
{slots.footer}
)} @@ -183,31 +202,40 @@ export const CardWithHeadlineComponent: React.FC< CardWithHeadlineComponentProps > = ({ sectionHeadline, languageCode, ...props }) => { const regionId = useId(); + const alg$ = useDecoderStore((state) => state.alg); return (
{sectionHeadline && ( - <> -

- {sectionHeadline.title} - {sectionHeadline.titleTag && ( - - {sectionHeadline.titleTag} - +
+
+

+ {sectionHeadline.title} + {sectionHeadline.titleTag && ( + + {sectionHeadline.titleTag} + + )} +

+ {sectionHeadline.description && ( +

+ {sectionHeadline.description} +

)} -

- {sectionHeadline.description && ( -

- {sectionHeadline.description} -

+
+ + {isHmacAlg(alg$) ? ( + + ) : ( + )} - +

)}
diff --git a/src/features/common/components/card/card.module.scss b/src/features/common/components/card/card.module.scss index a320b31e..a7104dc9 100644 --- a/src/features/common/components/card/card.module.scss +++ b/src/features/common/components/card/card.module.scss @@ -2,17 +2,17 @@ @use "@/libs/theme/styles/mixins" as *; .cardHeadline__title { - grid-column: 1 / -1; - display: flex; - align-items: center; - gap: 0.25rem; - text-transform: uppercase; - padding: 0.5rem 0; - color: var(--color_fg_default); - font-size: 0.875rem; - line-height: 1.375rem; - font-weight: 500; - letter-spacing: 0.24px; + grid-column: 1/-1; + display: flex; + align-items: center; + gap: .25rem; + font-family: var(--font-primary); + padding-top: .75rem; + color: var(--color_fg_default); + font-size: 1rem; + line-height: 1.15rem; + font-weight: 700; + letter-spacing: -.2px; & * { font-size: inherit; @@ -56,24 +56,44 @@ } } +.headline__container { + display: flex; + flex-direction: column; + gap: 0; +} + +.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; + flex: 1 1; grid-column: span 6; display: flex; - flex: 1; flex-direction: column; - border-radius: 0.5rem; - border: 1px solid var(--color_border_bold); - background: var(--color_bg_code-editor); - overflow: hidden; - - &[data-type="output"] { - background: var(--color_bg_layer_alternate); - } + border-radius: 1rem; + border: 1px solid var(--color_border_default); + background: var(--color_bg_layer_alternate-bold); + padding: 0.25rem; + letter-spacing: -.1px; +} - &[data-frameless="true"] { - border-radius: 0; - border: none; +.card__heading_title_container { + display: flex; + flex-direction: row; + align-items: center; + svg { + color: var(--color_fg_default); + margin-right: 2px; } } @@ -106,10 +126,7 @@ justify-content: space-between; padding: 0.5rem 1rem; height: 3rem; - - border-bottom: 1px solid var(--color_border_bold); gap: 0.25rem; - text-transform: uppercase; } .card__fullTitle { @@ -143,22 +160,19 @@ } .card__titleKey { - color: var(--color_fg_default); - font-size: 0.875rem; - line-height: 1.375rem; - font-style: normal; - font-weight: 500; - letter-spacing: 0.24px; - text-transform: uppercase; + color: var(--color_fg_default); + font-size: .875rem; + line-height: 1.375rem; + font-weight: 500; + letter-spacing: .24px; } .card__titleValue { color: var(--color_fg_default); - font-size: 0.875rem; + font-size: .875rem; line-height: 1.375rem; font-weight: 500; - letter-spacing: 0.24px; - text-transform: uppercase; + letter-spacing: .24px; } .card__action { @@ -173,6 +187,13 @@ border-top: 1px solid var(--color_border_bold); } +.card__content { + background-color: var(--color_bg_layer); + height: 100%; + border-radius: .75rem; + box-shadow: 0 0 0 .5px rgba(0,0,0,.12),0 2px 2px -1px rgba(0,0,0,.04),0 4px 4px -2px rgba(0,0,0,.04),0 6px 6px -3px rgba(0,0,0,.04); +} + .card__body { display: flex; flex: 1; @@ -197,20 +218,19 @@ } .card__status { + flex: 1 1; display: flex; - flex-direction: column; - flex-shrink: 0; - padding: 0.5rem 1rem; - gap: 0.25rem; - font-size: 0.875rem; + align-items: center; + justify-content: flex-start; + padding: .75rem .75rem .5rem; + gap: .25rem; + font-size: .875rem; line-height: 1.375rem; color: var(--color_fg_default); - border-top: 1px solid var(--color_border_bold); } .card__success { - background-color: var(--color_bg_state_success_subtle); - + svg, span, p, code { @@ -227,8 +247,7 @@ } .card__error { - background-color: var(--color_bg_state_danger_subtle); - + span, p, code { @@ -245,7 +264,6 @@ } .card__warning { - background-color: var(--color_bg_state_caution_subtle); span, p, diff --git a/src/features/common/components/checkbox/checkbox.module.scss b/src/features/common/components/checkbox/checkbox.module.scss index 307edf43..94b4e498 100644 --- a/src/features/common/components/checkbox/checkbox.module.scss +++ b/src/features/common/components/checkbox/checkbox.module.scss @@ -2,6 +2,7 @@ --selected-color: var(--color_bg_state_success); --selected-color-pressed: var(--color_fg_on_state_success_subtle); --checkmark-color: var(--color_border_state_success); + width: 250px; display: flex; /* This is needed so the HiddenInput is positioned correctly */ diff --git a/src/features/common/components/context-menu/context-menu.module.scss b/src/features/common/components/context-menu/context-menu.module.scss new file mode 100644 index 00000000..8bfe011b --- /dev/null +++ b/src/features/common/components/context-menu/context-menu.module.scss @@ -0,0 +1,61 @@ +.container { + max-width: calc(-24px + 100vw); + max-height: calc(-24px + 100vh); + position: fixed; + background-color: var(--color_bg_layer); + border: 1px solid var(--color_border_default); + font-size: 0.875rem; + z-index: 9009; + cursor: default; + border-radius: 1rem; + overflow: hidden; + padding: 0.25rem; + min-width: 180px; + box-shadow: + 0 1px 1px -0.5px rgba(0, 0, 0, 0.04), + 0 3px 3px -1.5px rgba(0, 0, 0, 0.04), + 0 6px 6px -3px rgba(0, 0, 0, 0.04), + 0 12px 12px -6px rgba(0, 0, 0, 0.04), + inset 0 0 0 1px rgba(0, 0, 0, 0.04); +} + +.groupLabel { + width: 100%; + padding: 0.5rem 0.75rem 0.25rem; + font-size: 0.8125rem; + color: var(--color_fg_default); +} + +.list { + display: flex; + align-items: center; + list-style-type: none; + flex-direction: column; +} + +.menuItem { + width: 100%; + justify-content: flex-start; + position: relative; + padding: 0.5rem 0.75rem; + border-radius: 0.75rem; + color: var(--color_fg_bold); + gap: 0.5rem; + font-family: var(--font-primary); + border: none; + background-color: var(--color_bg_layer); + display: flex; + align-items: center; + + &:hover { + cursor: pointer; + background-color: var(--color_bg_layer_alternate-bold); + } +} + +.separator { + width: 100%; + height: 1px; + background-color: var(--color_border_default); + margin: 0.25rem 0; +} diff --git a/src/features/common/components/context-menu/context-menu.tsx b/src/features/common/components/context-menu/context-menu.tsx new file mode 100644 index 00000000..8ecb97ba --- /dev/null +++ b/src/features/common/components/context-menu/context-menu.tsx @@ -0,0 +1,101 @@ +"use client"; + +import React, { FC } from "react"; +import { BrandDictionaryModel } from "@/features/localization/models/brand-dictionary.model"; +import styles from "./context-menu.module.scss"; +import jwtLogoString from "@/public/img/jwt-logo.svg?raw"; +import jwtSymbolString from "@/public/img/jwt-symbol.svg?raw"; +import jwtWordmarkString from "@/public/img/jwt-wordmark.svg?raw"; +import { Button } from "react-aria-components"; +import { CopyIcon } from "../icons/copy/copy-icon"; +import { DownloadIconComponent } from "../../assets/download-icon.component"; + +interface ContextMenuProps { + dictionary: BrandDictionaryModel; + position: { x: number; y: number } | null; + setIsCopied: (value: React.SetStateAction) => void; +} + +const ContextMenu: FC = ({ + dictionary, + position, + setIsCopied, +}) => { + if (!position) return null; + + const handleIconCopy = async (svgString: string) => { + if (!navigator.clipboard) { + console.error("Clipboard API not available"); + return; + } + try { + await navigator.clipboard.writeText(svgString); + setIsCopied(true); + } catch (err) { + console.error("Failed to copy SVG: ", err); + } + }; + + return ( +
+
{dictionary.menu.brand.label}
+
+ + + + {dictionary.menu.brand.svg.downloadLabel} + + + + + {dictionary.menu.brand.symbol.downloadLabel} + + + + + {dictionary.menu.brand.wordmark.downloadLabel} + +
+
+
{dictionary.menu.tools.label}
+ {dictionary.menu.tools.items.map((el) => ( + {el.label} + ))} +
+ ); +}; + +export default ContextMenu; 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 df256aa4..c62873c2 100644 --- a/src/features/common/components/debugger-picker/debugger-picker.component.tsx +++ b/src/features/common/components/debugger-picker/debugger-picker.component.tsx @@ -5,7 +5,6 @@ import { DebuggerPickerOptionModel } from "@/features/common/models/debugger-pic import { LibraryFilterLabel } from "@/features/libraries/models/library-filters.model"; import { isGroupedOptionsType } from "./utils"; - interface PickerLabelProps { label: string | null; } @@ -17,12 +16,12 @@ const getGroupLabel = ( >, selected: DebuggerPickerOptionModel ): LibraryFilterLabel | undefined => { - if(!isGroupedOptionsType(options)) return undefined + if (!isGroupedOptionsType(options)) return undefined; const group = (options as GroupBase[]).find( (group) => group.options.some((opt) => opt.value === selected.value) ); - return group ? group.label as LibraryFilterLabel : undefined; + return group ? (group.label as LibraryFilterLabel) : undefined; }; const PickerLabel: React.FC = ({ label }) => { @@ -41,7 +40,7 @@ interface DebuggerPickerComponentProps { GroupBase >; isGrouped?: boolean; - selectedOptionCode: DebuggerPickerOptionModel | null; + selectedOptionCode?: DebuggerPickerOptionModel | null; handleSelection: ( selection: string, parentLabel?: LibraryFilterLabel @@ -55,16 +54,14 @@ export const DebuggerPickerComponent: React.FC< > = ({ label, options, - handleSelection, selectedOptionCode, + handleSelection, placeholder, minWidth, }) => { const [isClient, setIsClient] = useState(false); - const handleChange = ( - selection: SingleValue - ) => { + const handleChange = (selection: SingleValue) => { if (!selection) { return; } @@ -78,31 +75,45 @@ export const DebuggerPickerComponent: React.FC< if (!isClient) { return ( -
+ <>
-
+ ); } return ( -
+ <> {label && } -
+ ); }; diff --git a/src/features/common/components/debugger-picker/debugger-picker.module.scss b/src/features/common/components/debugger-picker/debugger-picker.module.scss index 8541f752..ab25a72c 100644 --- a/src/features/common/components/debugger-picker/debugger-picker.module.scss +++ b/src/features/common/components/debugger-picker/debugger-picker.module.scss @@ -21,8 +21,8 @@ $picker-list-offset-lg: calc(($picker-list-width-lg - $picker-width-lg) / 2); background: transparent; border: none; color: var(--color_fg_bold); - cursor: pointer; - gap: 0.75rem; + gap: .75rem; + width: 100%; //width: $picker-width-sm; @@ -34,20 +34,17 @@ $picker-list-offset-lg: calc(($picker-list-width-lg - $picker-width-lg) / 2); } .picker__label { - display: flex; - align-items: center; - justify-content: center; - padding: 0; - list-style-type: none; - margin: 0; - gap: 0.5rem; - font-size: 0.8125rem; - line-height: 1.3125rem; - letter-spacing: -0.05px; - - width: 100%; - - height: 100%; + display: flex; + align-items: center; + justify-content: flex-start; + padding: 0; + list-style-type: none; + margin: 0; + gap: .5rem; + font-size: .875rem; + line-height: 1.35; + width: 100%; + height: 100%; & svg { stroke: var(--color_fg_bold); @@ -56,4 +53,5 @@ $picker-list-offset-lg: calc(($picker-list-width-lg - $picker-width-lg) / 2); .picker__fullName { display: flex; -} + font-weight: 600; +} \ No newline at end of file diff --git a/src/features/common/components/ebook/ebook.component.tsx b/src/features/common/components/ebook/ebook.component.tsx index b76bdd2b..8a0b20e0 100644 --- a/src/features/common/components/ebook/ebook.component.tsx +++ b/src/features/common/components/ebook/ebook.component.tsx @@ -22,11 +22,44 @@ export const EbookComponent: React.FC = ({ wrapperClassName={styles.wrapper} contentClassName={styles.content} > +
+        
+          {`{\n       "sub"`}
+          : 
+          {`"1234567890"`}
+          ,
+        
+        
+          {" "}
+          {`      "name"`}
+          : 
+          {`"John Doe"`}
+          ,
+        
+        
+          {" "}
+          {`      "admin"`}
+          : 
+          true
+          ,
+        
+        
+          {" "}
+          {`      "iat"`}
+          : 
+          1516239022
+        
+        {`}`}
+      
{dictionary.title} diff --git a/src/features/common/components/ebook/ebook.module.scss b/src/features/common/components/ebook/ebook.module.scss index f17afce6..ca108896 100644 --- a/src/features/common/components/ebook/ebook.module.scss +++ b/src/features/common/components/ebook/ebook.module.scss @@ -2,10 +2,10 @@ @use "@/libs/theme/styles/mixins" as *; .container { - @include Container; - position: relative; - overflow: hidden; - background: var(--color_bg_auth0-cta); + width: 100%; + position: relative; + margin: 4rem 0; + overflow: clip; &:before { position: absolute; @@ -14,99 +14,75 @@ content: ""; width: 100%; height: 100%; - - background: url("https://cdn.auth0.com/website/passkeys-playground/assets/cta-mobile.png") - no-repeat; - background-size: cover; - } - - @media only screen and (min-width: 480px) { - &:before { - background: url("https://cdn.auth0.com/website/passkeys-playground/assets/cta-mobile.png") - no-repeat; - background-size: cover; - } - } - - @media #{$breakpoint-dimension-sm} { - &:before { - background: url("/images/auth0-background.svg") no-repeat right - center; - background-size: cover; - } } @media #{$breakpoint-dimension-md} { - background: unset; - - &:before { - background: none; - } - - &:after { - display: none; - background: none; - } + overflow: unset; } + } .wrapper { @include ExtendedGrid; position: relative; justify-content: center; - - border-radius: 0; - overflow: hidden; - - @media #{$breakpoint-dimension-xl} { - border-radius: 1.5rem; - } + border-radius: 1rem; } .content { - @include InnerContentGrid; + display: flex; + width: calc(100% - 2rem); + max-width: 1312px; + margin: 0 auto; position: relative; padding: 1.5rem 0 14rem; - isolation: isolate; + border-radius: 1rem; + border: 1px dashed var(--color_border_bold); + background-color: var(--color_bg_layer_alternate); + background-image: linear-gradient(90deg,hsla(0,0%,50%,.071) 1px,transparent 0),linear-gradient(180deg,hsla(0,0%,50%,.071) 1px,transparent 0); + background-size: 24px 24px; + &:after { position: absolute; content: ""; - bottom: -1.5rem; - right: 0; - background: url("/images/ebook-image.png") center no-repeat; - background-size: contain; - width: 100%; - height: 75%; + top: 10rem; + left: 18rem; + background-image: url("/images/ebook.svg"); + border-radius: .25rem; + transform: rotate(-7.5deg); + height: 320px; + width: 240px; + z-index: 10; } - @media #{$breakpoint-dimension-xs} { - padding-bottom: 1.5rem; - column-gap: 6rem; + &:hover:after { + perspective: 100px; + transform: rotate(5deg) translateX(-24px) translateY(0) scale(1.1); + transition: all .2s ease-in-out; + } - &:after { - position: absolute; - content: ""; - top: 0; - right: 0; - background: url("/images/ebook-image.png") center no-repeat; - background-size: cover; - height: 100%; - width: 32%; - z-index: 10; - } + @media #{$breakpoint-dimension-xs} { + column-gap: 0; } @media #{$breakpoint-dimension-xs} { column-gap: 0; } + @media only screen and (min-width: 992px) and (min-width: 768px) { + width: calc(100% - 4rem); + grid-template-columns: repeat(12, minmax(0, 1fr)); + } + @media #{$breakpoint-dimension-md} { - background: var(--color_bg_auth0-cta); - padding: 2rem 2.5rem; + padding: 4rem 2.5rem; border-radius: 1rem; - overflow: hidden; - + display: grid; + max-width: 1312px; + margin: 0 auto; + transition: all .2s ease-in-out; + &:before { position: absolute; bottom: 0; @@ -114,68 +90,175 @@ content: ""; width: 100%; height: 100%; - background-size: contain; + border-radius: 1rem; + background-color: var(--color_bg_layer_alternate); + background-image: linear-gradient(90deg,hsla(0,0%,50%,.071) 1px,transparent 0),linear-gradient(180deg,hsla(0,0%,50%,.071) 1px,transparent 0); + background-size: 24px 24px; + } - background: url("/images/auth0-background.svg") no-repeat right - bottom; - background-size: cover; + &:after { + position: absolute; + content: ""; + top: -1.75rem; + left: 4rem; + background-image: url("/images/ebook.svg"); + border-radius: .25rem; + transform: rotate(-7.5deg); + height: 320px; + width: 240px; + z-index: 10; + transition: all .2s ease-in-out; } } + + @media #{$breakpoint-dimension-sm} { + width: calc(100% - 4rem); + } +} + +.ebookBanner__code { + pointer-events: none; + position: absolute; + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: center; + grid-column: 3/span 3; + height: fit-content; + transform: translateY(0); + top: auto; + bottom: 0; + left: 0; + width: 100%; + text-wrap: wrap; + padding: 1rem; + background: #191919; + color: var(--color_fg_code); + border-radius: 1.25rem; + border-top-left-radius: .25rem; + border-top-right-radius: .25rem; + border: 1px solid var(--color_border_bold); + font-size: .875rem; + white-space: pre; + word-wrap: break-word; + text-overflow: ellipsis; + text-wrap-mode: wrap; + text-wrap-style: balance; + overflow: clip; + box-shadow: 0 0 0 1px rgba(0,0,0,.12),0 4px 4px -2px rgba(0,0,0,.04),0 2px 2px -1px rgba(0,0,0,.04); + transition: all .4s ease-in-out; + + span { + padding: .25rem 0; + transition: color .1s ease; + } + + @media #{$breakpoint-dimension-md} { + border-radius: 1.25rem; + left: 0; + top: 50%; + transform: translateY(-50%); + } +} + +.ebookBanner__code_dot { + display: inline; + position: relative; + color: var(--color_border_inverse_static); + padding: .125rem 0; +} + +.ebookBanner__code_string { + display: inline; + position: relative; + color: var(--color_jwt_signature); + padding: .125rem 0; +} + +.ebookBanner__code_boolean { + display: inline; + position: relative; + color: var(--color_jwt_header); + padding: .125rem 0; +} + +.ebookBanner__code_number { + display: inline; + position: relative; + color: var(--color_jwt_payload); + padding: .125rem 0; } .ebookBanner__copy { - grid-column: 1 / span 6; + grid-column: 1/span 6; display: flex; flex-direction: column; + padding: 0 2.5rem; z-index: 1; + margin-top: 2rem; @media #{$breakpoint-dimension-xs} { - grid-column: 1 / span 4; + grid-column: 1/span 4; } @media #{$breakpoint-dimension-sm} { - grid-column: 1 / span 8; + grid-column: 6/span 6; + } + + @media #{$breakpoint-dimension-md} { + margin-top: 0; } } .ebookBanner__title { - color: var(--color_fg_bold); - + width: 100%; + margin: 0; + text-align: left; font-size: 1.5rem; - line-height: 2rem; - letter-spacing: 0.05px; + line-height: 1.15; font-weight: 500; + color: var(--color_fg_bold); + font-feature-settings: lining-nums proportional-nums; + font-variant-numeric: lining-nums proportional-nums; + font-style: normal; + + @media #{$breakpoint-dimension-lg} { + font-size: 2.5rem; + } } .ebookBanner__description { color: var(--color_fg_default); font-size: 1rem; - line-height: 1.25rem; - margin-top: 0.5rem; + line-height: 1.5; + margin-top: .5rem; } .ebookBanner__link { - margin-top: 1.5rem; - display: flex; - padding: 0.5rem 1rem; - justify-content: center; - align-items: center; - gap: 0.5rem; - align-self: stretch; - - border-radius: 0.375rem; - background: linear-gradient( - 120deg, - #191919 -29.78%, - #4016a0 11.61%, - #3f59e4 100.07% - ); - - color: $neutrals-light-100-snow; - font-size: 0.875rem; - line-height: 1.375rem; - font-weight: 500; - letter-spacing: 0.2px; + width: fit-content; + margin-top: 1.5rem; + display: flex; + padding: .25rem .75rem; + justify-content: center; + align-items: center; + gap: .5rem; + align-self: stretch; + border-radius: 9999px; + background: var(--color_fg_on_button); + box-shadow: 0 1px 1px -.5px rgba(0,0,0,.04),0 3px 3px -1.5px rgba(0,0,0,.04),0 6px 6px -3px rgba(0,0,0,.04),0 12px 12px -6px rgba(0,0,0,.04),0 24px 24px -12px rgba(0,0,0,.04),inset 0 0 0 1px rgba(0,0,0,.08); + color: var(--color_bg_layer); + font-size: .875rem; + font-weight: 700; + line-height: 1.375rem; + font-weight: 500; + letter-spacing: -.2px; + transition: all .3s ease-in-out; + + &:hover { + background: var(--color_fg_on_button_primary); + box-shadow: 0 0 0 4px rgba(0,0,0,.2); + transition: all .2s ease-out; + } :global(html[lang="ja"]) & { @include LocaleLineHeight("ja", 1rem); @@ -203,6 +286,6 @@ svg { height: 0.75rem; width: 0.75rem; - fill: $neutrals-light-100-snow; + fill: var(--color_bg_layer); } } 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..3e9277ae 100644 --- a/src/features/common/components/footer/footer.component.tsx +++ b/src/features/common/components/footer/footer.component.tsx @@ -1,6 +1,12 @@ "use client"; -import React, { MouseEvent, useState } from "react"; +import React, { MouseEvent, useEffect, useState } from "react"; +import Select, { + SingleValue, + OptionsOrGroups, + GroupBase, + NonceProvider, +} from "react-select"; import { FooterIconsComponent } from "./footer-Icons.component"; import { MonoFont, SecondaryFont } from "@/libs/theme/fonts"; import Image from "next/image"; @@ -15,25 +21,43 @@ 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 { getBrandDictionary } from "@/features/localization/services/brand-dictionary.service"; +import { savePreferredLanguage } from "@/features/localization/services/ui-language.utils"; +import { UiLanguageModel } from "../../models/ui-language.model"; +import { GlobeIconComponent } from "../bars/ribbon/assets/globe-icon.component"; interface FooterComponentProps { languageCode: string; dictionary: LayoutDictionaryModel["footer"]; - auth0Logo: StaticImageMetadataModel; - siteLogo: React.ReactNode; } export const FooterComponent: React.FC = ({ languageCode, dictionary, - auth0Logo, - siteLogo, }) => { + const currentLanguage = dictionary.languagePicker.options.filter( + (element) => element.value === languageCode + )[0]; + + const handleChange = (selection: SingleValue) => { + if (!selection) { + return; + } + savePreferredLanguage(selection.value); + }; + const [modalState, setModalState] = useState( - ModalStateValues.CLOSED, + ModalStateValues.CLOSED ); + const [portalTarget, setPortalTarget] = useState(null); + + useEffect(() => { + setPortalTarget(document.body); + }, []); + + const images = getBrandDictionary(languageCode); const languagePathPrefix: string = languageCode === DEFAULT_LANGUAGE_CODE @@ -66,9 +90,10 @@ export const FooterComponent: React.FC = ({ contentClassName={styles.content} >
- - {siteLogo} - +
@@ -114,7 +139,7 @@ export const FooterComponent: React.FC = ({ }} className={clsx( styles.resource__button, - SecondaryFont.className, + SecondaryFont.className )} > {trigger.text} @@ -162,21 +187,80 @@ export const FooterComponent: React.FC = ({ target="_blank" href="https://auth0.com/" > - {auth0Logo.alt} + {dictionary.copyright} + {dictionary.languagePicker.options.length > 1 && ( +
+ + + + +
+
+ ); +}; diff --git a/src/features/decoder/components/jwt-editor.component.tsx b/src/features/decoder/components/jwt-editor.component.tsx index 36f05a36..3475fc06 100644 --- a/src/features/decoder/components/jwt-editor.component.tsx +++ b/src/features/decoder/components/jwt-editor.component.tsx @@ -73,7 +73,7 @@ export const JwtEditorComponent: React.FC = ({ padding="1rem" style={{ fontFamily: '"Roboto Mono", monospace', - fontSize: 14, + fontSize: 16, lineHeight: 1.4, border: "none", outline: "none", diff --git a/src/features/decoder/components/jwt-input.component.tsx b/src/features/decoder/components/jwt-input.component.tsx index 17274b92..c906aae5 100644 --- a/src/features/decoder/components/jwt-input.component.tsx +++ b/src/features/decoder/components/jwt-input.component.tsx @@ -72,7 +72,7 @@ export const JwtInputComponent: React.FC = ({ isSelected={autoFocusEnabled} onChange={(e) => handleCheckboxChange(e)} > - Enable auto-focus + {dictionary.autoFocusLabel}
= ({ success: [dictionary.successMessage], errors: decodeErrors$, }} + hasHeaderIcon slots={{ notification: ( = ({ value={token} /> ), diff --git a/src/features/decoder/components/jwt-input.module.scss b/src/features/decoder/components/jwt-input.module.scss index 386b07eb..09165b65 100644 --- a/src/features/decoder/components/jwt-input.module.scss +++ b/src/features/decoder/components/jwt-input.module.scss @@ -1,14 +1,12 @@ .headline { - grid-column: 1 / -1; - display: flex; - align-items: center; - gap: 0.25rem; - text-transform: uppercase; - color: var(--color_fg_default); - font-size: 0.875rem; - line-height: 1.375rem; - font-weight: 500; - letter-spacing: 0.24px; + display: flex; + align-items: center; + width: 100%; + font-size: 1rem; + font-weight: 700; + line-height: 1.15; + letter-spacing: -.1px; + color: var(--color_fg_default); } .checkbox__label { diff --git a/src/features/decoder/components/secret-key-input.component.tsx b/src/features/decoder/components/secret-key-input.component.tsx index 43eda43e..35a937b5 100644 --- a/src/features/decoder/components/secret-key-input.component.tsx +++ b/src/features/decoder/components/secret-key-input.component.tsx @@ -1,10 +1,8 @@ import React, { ChangeEvent, useEffect, useState } from "react"; -import { TokenDecoderEncodingFormatPickerComponent } from "@/features/decoder/components/token-decoder-encoding-format-picker.component"; import { SignatureVerificationSecretInputComponent } from "@/features/decoder/components/signature-verification-secret-input.component"; import { SignatureVerificationPublicKeyInputComponent } from "@/features/decoder/components/signature-verification-public-key-input.component"; import { CardWithHeadlineComponent } from "@/features/common/components/card/card.component"; import { DEFAULT_JWT } from "@/features/decoder/services/token-decoder.service"; -import { TokenDecoderKeyFormatPickerComponent } from "@/features/decoder/components/token-decoder-key-format-picker.component"; import { useDecoderStore } from "@/features/decoder/services/decoder.store"; import { HomeDictionaryModel } from "@/features/localization/models/home-dictionary.model"; import { useDebuggerStore } from "@/features/debugger/services/debugger.store"; @@ -20,6 +18,7 @@ import { CardToolbarCopyButtonComponent } from "@/features/common/components/car 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"; + type SecretKeyInputComponentProps = { languageCode: string; dictionary: HomeDictionaryModel["decoder"]["signatureVerification"]; @@ -58,7 +57,7 @@ export const SecretKeyInputComponent: React.FC< decoderInputs$.algType === SigningAlgCategoryValues.SYMMETRIC && decoderInputs$.symmetricSecretKey ? decoderInputs$.symmetricSecretKey - : DEFAULT_JWT.secret, + : DEFAULT_JWT.secret ); const [publicKey, setPublicKey] = useState(""); @@ -128,6 +127,7 @@ export const SecretKeyInputComponent: React.FC< ? dictionary.description.secret : dictionary.description.publicKey, }} + hasHeaderIcon languageCode={languageCode} messages={{ errors: verificationInputErrors$, @@ -137,7 +137,7 @@ export const SecretKeyInputComponent: React.FC< : dictionary.editor.successMessage.publicKey, ], }} - options={{ noPadding: true }} + options={{ noPadding: false }} title={ isHmacAlg(alg$) ? dictionary.editor.title.secret @@ -161,14 +161,7 @@ export const SecretKeyInputComponent: React.FC< isDisabled={isHmacAlg(alg$) ? !secret : !publicKey} /> - ), - footer: isHmacAlg(alg$) ? ( - - ) : ( - - ), + ) }} > {isHmacAlg(alg$) && ( diff --git a/src/features/decoder/components/secret-key-input.module.scss b/src/features/decoder/components/secret-key-input.module.scss index a4d81ca0..00a893a1 100644 --- a/src/features/decoder/components/secret-key-input.module.scss +++ b/src/features/decoder/components/secret-key-input.module.scss @@ -14,17 +14,17 @@ } .grid__subtitle { - grid-column: 1 / -1; - display: flex; - align-items: center; - gap: 0.25rem; - text-transform: uppercase; - padding: 0.5rem 0; - color: var(--color_fg_default); - font-size: 0.875rem; - line-height: 1.375rem; - font-weight: 500; - letter-spacing: 0.24px; + grid-column: 1/-1; + display: flex; + align-items: center; + gap: .25rem; + font-family: var(--font-primary); + padding-top: .75rem; + color: var(--color_fg_default); + font-size: 1rem; + line-height: 1.15rem; + font-weight: 700; + letter-spacing: -.2px; & * { font-size: inherit; diff --git a/src/features/decoder/components/token-decoder-key-format-picker.module.scss b/src/features/decoder/components/token-decoder-key-format-picker.module.scss index c172cbca..d85867ad 100644 --- a/src/features/decoder/components/token-decoder-key-format-picker.module.scss +++ b/src/features/decoder/components/token-decoder-key-format-picker.module.scss @@ -12,11 +12,17 @@ } .label { - color: var(--color_fg_default); - font-size: 0.875rem; - line-height: 1.375rem; - font-weight: 500; - letter-spacing: 0.1px; + display: flex; + align-items: center; + justify-content: flex-start; + padding: 0; + list-style-type: none; + margin: 0; + gap: .5rem; + font-size: .875rem; + line-height: 1.35; + width: 100%; + height: 100%; } .select { diff --git a/src/features/decoder/components/token-decoder-signature-validation.component.tsx b/src/features/decoder/components/token-decoder-signature-validation.component.tsx index 369943f9..5b20fa80 100644 --- a/src/features/decoder/components/token-decoder-signature-validation.component.tsx +++ b/src/features/decoder/components/token-decoder-signature-validation.component.tsx @@ -5,6 +5,7 @@ import { clsx } from "clsx"; import { useDecoderStore } from "@/features/decoder/services/decoder.store"; import { JwtSignatureStatusValues } from "@/features/common/values/jwt-signature-status.values"; import { CardMessageComponent } from "@/features/common/components/card-message/card-message.component"; +import { CheckIcon } from "@/features/common/components/icons/check/check-icon"; interface TokenDecoderSignatureValidationComponentProps { id: string; @@ -35,6 +36,7 @@ export const TokenDecoderSignatureValidationComponent: React.FC< role="status" className={clsx(styles.container, styles.valid)} > + {StringValues.editor.signatureVerified}
); diff --git a/src/features/decoder/components/token-decoder-signature-validation.module.scss b/src/features/decoder/components/token-decoder-signature-validation.module.scss index 2d383902..9681555d 100644 --- a/src/features/decoder/components/token-decoder-signature-validation.module.scss +++ b/src/features/decoder/components/token-decoder-signature-validation.module.scss @@ -1,25 +1,25 @@ @use "@/libs/theme/styles/variables" as *; .container { + flex: 1 1; display: flex; - flex-direction: column; - transition: background-color 0.13s ease-in-out; - padding: 0.5rem 1rem; - font-size: 0.75rem; - line-height: 1.125rem; + align-items: center; + justify-content: flex-start; + padding: .75rem .75rem .5rem; + gap: .25rem; + font-size: .875rem; + line-height: 1.375rem; + color: var(--color_fg_default); } .valid { - background-color: var(--color_bg_state_success_subtle); - color: var(--color_fg_on_state_success_subtle); + color: var(--color_fg_on_state_success_subtle); } .invalid { - background-color: var(--color_bg_state_danger_subtle); color: var(--color_fg_on_state_danger_subtle); } .warning { - background-color: var(--color_bg_state_caution_subtle); color: var(--color_fg_on_state_caution_subtle); } diff --git a/src/features/decoder/components/token-decoder.component.tsx b/src/features/decoder/components/token-decoder.component.tsx index 721ef51d..27faea8e 100644 --- a/src/features/decoder/components/token-decoder.component.tsx +++ b/src/features/decoder/components/token-decoder.component.tsx @@ -20,6 +20,8 @@ import { AsymmetricKeyFormatValues } from "@/features/common/values/asymmetric-k import { WidgetComponent } from "@/features/common/components/widget/widget/widget.component"; import { dataTestidDictionary } from "@/libs/testing/data-testid.dictionary"; import { DebuggerWidgetValues } from "@/features/common/values/debugger-widget.values"; +import { WidgetAlgPickerComponent } from "@/features/debugger/components/debugger-alg-picker/debugger-alg-picker.component"; +import styles from "./token-decoder.module.scss"; interface TokenDecoderComponentProps { languageCode: string; @@ -49,7 +51,7 @@ export const TokenDecoderComponent: React.FC = ({ const loadDecoderInputs = useDecoderStore((state) => state.loadDecoderInputs); const handleJwtChange$ = useDecoderStore((state) => state.handleJwtChange); const showUseHashWarning$ = useDecoderStore( - (state) => state.showUseHashWarning, + (state) => state.showUseHashWarning ); useEffect(() => { @@ -184,41 +186,51 @@ export const TokenDecoderComponent: React.FC = ({ }, [decoderInputs$, loadDecoderInputs, headlineConfig.isVisible]); return ( - +
+ {dictionary.description} + - } - contentOutput={ - <> - - - + - - } - warnings={null} - /> + } + contentOutput={ + <> + + + + + } + warnings={null} + /> + ); }; diff --git a/src/features/decoder/components/token-decoder.module.scss b/src/features/decoder/components/token-decoder.module.scss index 39909fa5..b57e7dc2 100644 --- a/src/features/decoder/components/token-decoder.module.scss +++ b/src/features/decoder/components/token-decoder.module.scss @@ -1,2 +1,32 @@ @use "@/libs/theme/styles/variables" as *; @use "@/libs/theme/styles/mixins" as *; + +.input__description { + display: flex; + width: calc(100% - 2rem); + max-width: 1312px; + flex-direction: column; + grid-row-gap: 0; + margin: 0 auto 2rem; + color: var(--color_fg_default); + font-size: .875rem; + line-height: 1.375rem; + margin-bottom: .5rem; + + & strong { + font-weight: 500; + } + + @media #{$breakpoint-dimension-sm} { + width: calc(100% - 4rem); + grid-column: span 12; + flex-direction: row; + align-items: center; + justify-content: space-between; + } + + @media #{$breakpoint-dimension-lg} { + width: calc(100% - 8rem); + } + +} \ No newline at end of file diff --git a/src/features/encoder/components/encoded-jwt-output.component.tsx b/src/features/encoder/components/encoded-jwt-output.component.tsx index 6f167a10..3a01b5d7 100644 --- a/src/features/encoder/components/encoded-jwt-output.component.tsx +++ b/src/features/encoder/components/encoded-jwt-output.component.tsx @@ -6,6 +6,7 @@ import { HomeDictionaryModel } from "@/features/localization/models/home-diction import { dataTestidDictionary } from "@/libs/testing/data-testid.dictionary"; import { CardToolbarComponent } from "@/features/common/components/card-toolbar/card-toolbar.component"; import { CardToolbarCopyButtonComponent } from "@/features/common/components/card-toolbar-buttons/card-toolbar-copy-button/card-toolbar-copy-button.component"; +import styles from "./encoded-jwt-output.module.scss"; type EncodedJwtOutputComponentProps = { languageCode: string; @@ -20,28 +21,32 @@ export const EncodedJwtOutputComponent: React.FC< const encodingErrors = useEncoderStore((state) => state.encodingErrors); return ( - - - - ), - }} - > - - + <> +

{dictionary.heading}

+ + + + ), + }} + > + + + ); }; diff --git a/src/features/encoder/components/encoded-jwt-output.module.scss b/src/features/encoder/components/encoded-jwt-output.module.scss new file mode 100644 index 00000000..3e2c0344 --- /dev/null +++ b/src/features/encoder/components/encoded-jwt-output.module.scss @@ -0,0 +1,14 @@ +@use "@/libs/theme/styles/variables" as *; +@use "@/libs/theme/styles/mixins" as *; + +.headline { + display: flex; + grid-column: 1/-1; + width: 100%; + font-size: 1rem; + font-weight: 700; + line-height: 1.15; + letter-spacing: -.1px; + color: var(--color_fg_default); + padding-bottom: 0.5rem; +} \ No newline at end of file diff --git a/src/features/encoder/components/token-encoder-encoding-format-picker.component.tsx b/src/features/encoder/components/token-encoder-encoding-format-picker.component.tsx deleted file mode 100644 index 631dd252..00000000 --- a/src/features/encoder/components/token-encoder-encoding-format-picker.component.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React, { useEffect, useState } from "react"; -import styles from "./token-encoder-encoding-format-picker.module.scss"; -import { EncodingValues } from "@/features/common/values/encoding.values"; -import { useEncoderStore } from "@/features/encoder/services/encoder.store"; -import { getPickersUiDictionary } from "@/features/localization/services/ui-language-dictionary.service"; -import { DebuggerPickerComponent } from "@/features/common/components/debugger-picker/debugger-picker.component"; - -interface TokenEncoderEncodingFormatPickerComponentProps { - languageCode: string; -} - -export const TokenEncoderEncodingFormatPickerComponent: React.FC< - TokenEncoderEncodingFormatPickerComponentProps -> = ({ languageCode }) => { - const dictionary = getPickersUiDictionary(languageCode); - - const handleSymmetricSecretKeyEncodingChange = useEncoderStore( - (state) => state.handleSymmetricSecretKeyEncodingChange, - ); - const controlledSymmetricSecretKey = useEncoderStore( - (state) => state.controlledSymmetricSecretKey, - ); - - const [secretEncodingFormat, setSecretEncodingFormat] = - useState(EncodingValues.UTF8); - - useEffect(() => { - if (controlledSymmetricSecretKey) { - setSecretEncodingFormat(controlledSymmetricSecretKey.encoding); - } - }, [controlledSymmetricSecretKey]); - - const onSecretEncodingFormatChange = (value: string) => { - setSecretEncodingFormat(value as EncodingValues); - - handleSymmetricSecretKeyEncodingChange(value as EncodingValues); - }; - - return ( -
- -
- ); -}; diff --git a/src/features/encoder/components/token-encoder-encoding-format-picker.module.scss b/src/features/encoder/components/token-encoder-encoding-format-picker.module.scss deleted file mode 100644 index 1429bdc1..00000000 --- a/src/features/encoder/components/token-encoder-encoding-format-picker.module.scss +++ /dev/null @@ -1,70 +0,0 @@ -@use "@/libs/theme/styles/variables" as *; - -.container { - position: relative; - display: flex; - align-items: center; - gap: 0.5rem; - - @media #{$breakpoint-dimension-sm} { - align-self: unset; - } -} - -.label { - color: var(--color_fg_default); - font-size: 0.875rem; - line-height: 1.375rem; - font-weight: 500; - letter-spacing: 0.1px; -} - -.select { - display: flex; - padding: 0.125rem 1.75rem 0.125rem 0.875rem; - align-items: center; - gap: 0.75rem; - align-self: stretch; - - border-radius: 0.25rem; - border: 1px solid var(--color_border_button); - - color: var(--color_fg_default); - font-variant-numeric: slashed-zero; - font-feature-settings: - "clig" off, - "liga" off; - font-size: 0.875rem; - line-height: 1.375rem; - letter-spacing: 0.2px; - appearance: none; - - z-index: 2; - - background-color: transparent; -} - -.icon { - position: absolute; - top: 50%; - right: 0.5rem; - transform: translateY(-40%); - - svg { - height: 1rem; - width: 1rem; - - path { - fill: var(--color_fg_default); - } - } - - @media #{$breakpoint-dimension-sm} { - transform: translateY(-45%); - - svg { - height: 1.25rem; - width: 1.25rem; - } - } -} diff --git a/src/features/encoder/components/token-encoder-input.component.tsx b/src/features/encoder/components/token-encoder-input.component.tsx index e3300551..46f63c23 100644 --- a/src/features/encoder/components/token-encoder-input.component.tsx +++ b/src/features/encoder/components/token-encoder-input.component.tsx @@ -1,13 +1,18 @@ "use client"; -import React, { ChangeEvent, useCallback, useEffect, useState } from "react"; +import React, { + ChangeEvent, + Fragment, + useCallback, + useEffect, + useState, +} from "react"; import { HeaderEditorComponent } from "@/features/encoder/components/header-editor.component"; import { CardComponent, CardComponentProps, } from "@/features/common/components/card/card.component"; import { PayloadEditorComponent } from "@/features/encoder/components/payload-editor.component"; -import { TokenEncoderEncodingFormatPickerComponent } from "@/features/encoder/components/token-encoder-encoding-format-picker.component"; import { TokenEncoderKeyFormatPickerComponent } from "@/features/encoder/components/token-encoder-key-format-picker.component"; import { SigningSecretEditorComponent } from "@/features/encoder/components/signing-secret-editor.component"; import { SigningPrivateKeyEditorComponent } from "@/features/encoder/components/signing-private-key-editor.component"; @@ -28,6 +33,7 @@ import { import { dataTestidDictionary } from "@/libs/testing/data-testid.dictionary"; import { CardToolbarComponent } from "@/features/common/components/card-toolbar/card-toolbar.component"; import { CardToolbarClearButtonComponent } from "@/features/common/components/card-toolbar-buttons/card-toolbar-clear-button/card-toolbar-clear-button.component"; +import { EncodingFormatToggleSwitchComponent } from "@/features/decoder/components/encoding-format-toggle-swith/encoding-format-toggle-switch"; type HeaderInputComponentProps = { languageCode: string; @@ -41,32 +47,32 @@ export const TokenEncoderInputComponent: React.FC< const headerErrors$ = useEncoderStore((state) => state.headerErrors && state.headerErrors.length > 0 ? state.headerErrors - : null, + : null ); const headerWarnings$ = useEncoderStore((state) => state.headerWarnings && state.headerWarnings.length > 0 ? state.headerWarnings - : null, + : null ); const payloadErrors$ = useEncoderStore((state) => state.payloadErrors && state.payloadErrors.length > 0 ? state.payloadErrors - : null, + : null ); const signingErrors$ = useEncoderStore((state) => state.signingErrors && state.signingErrors.length > 0 ? state.signingErrors - : null, + : null ); const controlledHeader$ = useEncoderStore((state) => state.controlledHeader); const controlledPayload$ = useEncoderStore( - (state) => state.controlledPayload, + (state) => state.controlledPayload ); const controlledSymmetricSecretKey$ = useEncoderStore( - (state) => state.controlledSymmetricSecretKey, + (state) => state.controlledSymmetricSecretKey ); const controlledAsymmetricPrivateKey$ = useEncoderStore( - (state) => state.controlledAsymmetricPrivateKey, + (state) => state.controlledAsymmetricPrivateKey ); const [header, setHeader] = useState(DEFAULT_HEADER); @@ -95,28 +101,28 @@ export const TokenEncoderInputComponent: React.FC< }, [controlledAsymmetricPrivateKey$]); const handleHeaderChange$ = useEncoderStore( - (state) => state.handleHeaderChange, + (state) => state.handleHeaderChange ); const resetControlledHeader$ = useEncoderStore( - (state) => state.resetControlledHeader, + (state) => state.resetControlledHeader ); const handlePayloadChange$ = useEncoderStore( - (state) => state.handlePayloadChange, + (state) => state.handlePayloadChange ); const resetControlledPayload$ = useEncoderStore( - (state) => state.resetControlledPayload, + (state) => state.resetControlledPayload ); const handleSymmetricSecretKeyChange$ = useEncoderStore( - (state) => state.handleSymmetricSecretKeyChange, + (state) => state.handleSymmetricSecretKeyChange ); const resetControlledSymmetricSecretKey$ = useEncoderStore( - (state) => state.resetControlledSymmetricSecretKey, + (state) => state.resetControlledSymmetricSecretKey ); const handleAsymmetricPrivateKeyChange$ = useEncoderStore( - (state) => state.handleAsymmetricPrivateKeyChange, + (state) => state.handleAsymmetricPrivateKeyChange ); const resetControlledAsymmetricPrivateKey$ = useEncoderStore( - (state) => state.resetControlledAsymmetricPrivateKey, + (state) => state.resetControlledAsymmetricPrivateKey ); const clearHeader = async () => { @@ -153,7 +159,7 @@ export const TokenEncoderInputComponent: React.FC< handleHeaderChange$(cleanValue); }, - [handleHeaderChange$], + [handleHeaderChange$] ); const handlePayloadChange = useCallback( @@ -164,11 +170,11 @@ export const TokenEncoderInputComponent: React.FC< handlePayloadChange$(cleanValue); }, - [handlePayloadChange$], + [handlePayloadChange$] ); const handleSymmetricSecretKeyChange = async ( - e: ChangeEvent, + e: ChangeEvent ) => { const key = e.target.value; @@ -180,7 +186,7 @@ export const TokenEncoderInputComponent: React.FC< }; const handleAsymmetricPrivateKeyChange = async ( - e: ChangeEvent, + e: ChangeEvent ) => { const key = e.target.value; @@ -197,6 +203,7 @@ export const TokenEncoderInputComponent: React.FC< languageCode: languageCode, title: dictionary.headerEditor.title, compactTitle: dictionary.headerEditor.compactTitle, + hasHeaderIcon: true, children: ( ), + hasHeaderIcon: true, messages: { success: [dictionary.payloadEditor.successMessage], errors: payloadErrors$, @@ -255,6 +263,7 @@ export const TokenEncoderInputComponent: React.FC< cards.push({ id: dataTestidDictionary.encoder.secretKeyEditor.id, languageCode: languageCode, + hasHeaderIcon: true, title: isHmacAlg(alg$) ? dictionary.signatureEditor.title.secret : dictionary.signatureEditor.title.privateKey, @@ -307,13 +316,6 @@ export const TokenEncoderInputComponent: React.FC< /> ), - footer: isHmacAlg(alg$) ? ( - - ) : ( - - ), }, }); } @@ -322,7 +324,26 @@ export const TokenEncoderInputComponent: React.FC< <>
{cards.map((props) => ( - + + {props.id === dataTestidDictionary.encoder.secretKeyEditor.id ? ( +
+

{props.compactTitle}

+ {isHmacAlg(alg$) ? ( + + ) : ( + + )} +
+ ) : ( +

{props.compactTitle}

+ )} + +
))}
diff --git a/src/features/encoder/components/token-encoder-input.module.scss b/src/features/encoder/components/token-encoder-input.module.scss index 7ddf3a0d..30c16d15 100644 --- a/src/features/encoder/components/token-encoder-input.module.scss +++ b/src/features/encoder/components/token-encoder-input.module.scss @@ -24,3 +24,21 @@ display: none; } } +.headline_container { + display: flex; + grid-column: 1/-1; + width: 100%; + justify-content: space-between; + align-items: center; +} + +.headline { + display: flex; + grid-column: 1/-1; + width: 100%; + font-size: 1rem; + font-weight: 700; + line-height: 1.15; + letter-spacing: -.1px; + color: var(--color_fg_default); +} \ No newline at end of file diff --git a/src/features/encoder/components/token-encoder-key-format-picker.component.tsx b/src/features/encoder/components/token-encoder-key-format-picker.component.tsx index 523bcfc9..a005f448 100644 --- a/src/features/encoder/components/token-encoder-key-format-picker.component.tsx +++ b/src/features/encoder/components/token-encoder-key-format-picker.component.tsx @@ -15,14 +15,14 @@ export const TokenEncoderKeyFormatPickerComponent: React.FC< const dictionary = getPickersUiDictionary(languageCode); const handlePrivateKeyFormatChange = useEncoderStore( - (state) => state.handleAsymmetricPrivateKeyFormatChange, + (state) => state.handleAsymmetricPrivateKeyFormatChange ); const controlledAsymmetricPrivateKey$ = useEncoderStore( - (state) => state.controlledAsymmetricPrivateKey, + (state) => state.controlledAsymmetricPrivateKey ); const [keyFormat, setKeyFormat] = useState( - AsymmetricKeyFormatValues.PEM, + AsymmetricKeyFormatValues.PEM ); useEffect(() => { @@ -38,28 +38,32 @@ export const TokenEncoderKeyFormatPickerComponent: React.FC< }; return ( -
- +
+
+
+ +
+
); }; diff --git a/src/features/encoder/components/token-encoder-key-format-picker.module.scss b/src/features/encoder/components/token-encoder-key-format-picker.module.scss index 1429bdc1..f292b294 100644 --- a/src/features/encoder/components/token-encoder-key-format-picker.module.scss +++ b/src/features/encoder/components/token-encoder-key-format-picker.module.scss @@ -1,22 +1,47 @@ @use "@/libs/theme/styles/variables" as *; + +.encoder__switch { + display: flex; + width: 100%; + justify-content: flex-end; +} + .container { position: relative; display: flex; align-items: center; - gap: 0.5rem; + gap: .5rem; @media #{$breakpoint-dimension-sm} { align-self: unset; } } +.picker { + position: relative; + display: flex; + justify-content: space-between; + align-items: center; + background: transparent; + border: none; + color: var(--color_fg_bold); + gap: .75rem; + width: 100%; +} + .label { - color: var(--color_fg_default); - font-size: 0.875rem; - line-height: 1.375rem; - font-weight: 500; - letter-spacing: 0.1px; + display: flex; + align-items: center; + justify-content: flex-start; + padding: 0; + list-style-type: none; + margin: 0; + gap: .5rem; + font-size: .875rem; + line-height: 1.35; + width: 100%; + height: 100%; } .select { diff --git a/src/features/encoder/components/token-encoder.component.tsx b/src/features/encoder/components/token-encoder.component.tsx index d2a045bf..6ebe901d 100644 --- a/src/features/encoder/components/token-encoder.component.tsx +++ b/src/features/encoder/components/token-encoder.component.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useEffect, useRef } from "react"; +import styles from "./token-encoder.module.scss" import { EncodedJwtOutputComponent } from "@/features/encoder/components/encoded-jwt-output.component"; import { TokenEncoderInputComponent } from "@/features/encoder/components/token-encoder-input.component"; import { useEncoderStore } from "@/features/encoder/services/encoder.store"; @@ -10,6 +11,7 @@ import { SigningAlgCategoryValues } from "@/features/common/values/signing-alg-c import { WidgetComponent } from "@/features/common/components/widget/widget/widget.component"; import { dataTestidDictionary } from "@/libs/testing/data-testid.dictionary"; import { DebuggerWidgetValues } from "@/features/common/values/debugger-widget.values"; +import { WidgetAlgPickerComponent } from "@/features/debugger/components/debugger-alg-picker/debugger-alg-picker.component"; interface TokenEncoderComponentProps { languageCode: string; @@ -99,27 +101,37 @@ export const TokenEncoderComponent: React.FC = ({ }, [encoderInputs$, loadEncoderInputs, headlineConfig.isVisible]); return ( - +
+ {dictionary.description} + - } - contentOutput={ - - } - warnings={encodingWarnings$} - /> +
+ + } + contentOutput={ + + } + warnings={encodingWarnings$} + /> + ); }; diff --git a/src/features/encoder/components/token-encoder.module.scss b/src/features/encoder/components/token-encoder.module.scss new file mode 100644 index 00000000..b57e7dc2 --- /dev/null +++ b/src/features/encoder/components/token-encoder.module.scss @@ -0,0 +1,32 @@ +@use "@/libs/theme/styles/variables" as *; +@use "@/libs/theme/styles/mixins" as *; + +.input__description { + display: flex; + width: calc(100% - 2rem); + max-width: 1312px; + flex-direction: column; + grid-row-gap: 0; + margin: 0 auto 2rem; + color: var(--color_fg_default); + font-size: .875rem; + line-height: 1.375rem; + margin-bottom: .5rem; + + & strong { + font-weight: 500; + } + + @media #{$breakpoint-dimension-sm} { + width: calc(100% - 4rem); + grid-column: span 12; + flex-direction: row; + align-items: center; + justify-content: space-between; + } + + @media #{$breakpoint-dimension-lg} { + width: calc(100% - 8rem); + } + +} \ No newline at end of file diff --git a/src/features/home/components/assets/assets.component.tsx b/src/features/home/components/assets/assets.component.tsx index e1ba6e3a..441e7795 100644 --- a/src/features/home/components/assets/assets.component.tsx +++ b/src/features/home/components/assets/assets.component.tsx @@ -16,139 +16,53 @@ import iconWithLabelImg from "./jwt-label.icon.png"; import { createUrlPath } from "@/libs/utils/path.utils"; import { DEFAULT_LANGUAGE_CODE } from "@/features/localization/localization.config"; import { Button } from "react-aria-components"; - -interface DownloadButtonProps { - asset: StaticImageData; - filename: string; -} - -const DownloadButton: React.FC = ({ asset, filename }) => { - const downloadSVG = useCallback( - (image: StaticImageData, filename: string) => { - const link = document.createElement("a"); - link.href = image.src; - link.download = filename; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - }, - [], - ); - - return ( - - ); -}; +import { ArrowHeadIconComponent } from "@/features/common/assets/arrow-head-icon.component"; +import { Auth0DictionaryModel } from "@/features/localization/models/auth0-dictionary.model"; +import { Auth0CtaComponent } from "@/features/common/components/auth0-cta/auth0-cta.component"; type AssetsComponentProps = { languageCode: string; - dictionary: JwtDictionaryModel; + jwtDictionary: JwtDictionaryModel; + auth0Dictionary: Auth0DictionaryModel; }; export const AssetsComponent: React.FC = ({ languageCode, - dictionary, + jwtDictionary, + auth0Dictionary, }) => { const libraryLinkPath = createUrlPath([ languageCode === DEFAULT_LANGUAGE_CODE ? "" : languageCode, - dictionary.libraries.ctaButton.path - ? dictionary.libraries.ctaButton.path + jwtDictionary.libraries.ctaButton.path + ? jwtDictionary.libraries.ctaButton.path : "", ]); return ( - +

- {dictionary.libraries.title} + {jwtDictionary.libraries.title}

- {dictionary.libraries.description} + {jwtDictionary.libraries.description}

- {dictionary.libraries.ctaButton.label} + {jwtDictionary.libraries.ctaButton.label} +
-
-

- {dictionary.assets.badges.title} -

-
-
- {dictionary.assets.badges.images.viewOn.alt} - -
-
- {dictionary.assets.badges.images.compatible.alt} - -
-
-
-
-

- {dictionary.assets.logotype.title} -

-
-
- {dictionary.assets.logotype.images.icon.alt} - -
-
- {dictionary.assets.logotype.images.iconWithLabel.alt} - -
-
-
- + +
); }; diff --git a/src/features/home/components/assets/assets.module.scss b/src/features/home/components/assets/assets.module.scss index b4d0b408..5654af3f 100644 --- a/src/features/home/components/assets/assets.module.scss +++ b/src/features/home/components/assets/assets.module.scss @@ -6,22 +6,45 @@ } .content { - @include InnerContentGrid; - padding-top: 4rem; - padding-bottom: 4rem; + display: flex; + width: calc(100% - 2rem); + max-width: 1312px; + margin: 0 auto; + flex-direction: column; row-gap: 2rem; + @media #{$breakpoint-dimension-sm} { + width: calc(100% - 4rem); + grid-template-columns: repeat(12,minmax(0,1fr)); + } + + @media #{$breakpoint-dimension-lg} { + width: calc(100% - 128px); + display: grid; + grid-template-columns: repeat(12, minmax(0, 1fr)); + grid-column-gap: 1rem; + column-gap: 1rem; + width: calc(100% - 2rem); + max-width: 1312px; + margin: 0 auto; + grid-row-gap: unset; row-gap: unset; } + } .assets__column { - grid-column: span 6; + grid-column: span 1; color: var(--color_fg_default); display: flex; flex-direction: column; gap: 1.25rem; + border: 1px solid var(--color_border_default); + border-radius: 1rem; + padding: 2.5rem; + background-color: var(--color_bg_layer); + box-shadow: 0 2px 12px -4px rgba(0,0,0,.08),0 2px 1px -.5px rgba(0,0,0,.04),0 2px 4px -4px rgba(0,0,0,.05),inset -.5px -.5px 2px rgba(0,0,0,.04); @media #{$breakpoint-dimension-sm} { grid-column: span 4; @@ -30,14 +53,19 @@ } .assets__title { - color: var(--color_fg_bold); + width: 100%; + margin: 0; + text-align: left; font-size: 1.5rem; - line-height: 2rem; - font-style: normal; + line-height: 1.15; font-weight: 500; - letter-spacing: -0.5px; + color: var(--color_fg_bold); + font-feature-settings: lining-nums proportional-nums; + font-variant-numeric: lining-nums proportional-nums; + font-style: normal; - @media #{$breakpoint-dimension-sm} { + @media #{$breakpoint-dimension-lg} { + font-size: 2.5rem; } } @@ -49,22 +77,19 @@ .asset__link { width: fit-content; display: flex; - height: 48px; - padding: 1rem 1rem; + padding: .25rem .75rem; justify-content: center; align-items: center; - gap: 0.5rem; - - border-radius: 6px; - background: var(--color_bg_button_primary); - color: var(--color_fg_on_button_primary); - border: 1px solid var(--color_border_button); - - font-size: 1rem; - font-style: normal; - font-weight: 400; - line-height: 1.5rem; - letter-spacing: 0.32px; + gap: .25rem; + border-radius: 9999px; + background: var(--color_bg_layer); + border: 1px solid var(--color_border_default); + font-size: .875rem; + font-weight: 700; + line-height: 1.375rem; + font-weight: 500; + letter-spacing: -.2px; + transition: all .3s ease-in-out; &:focus-visible { outline: solid 1px var(--color_border_focus); @@ -72,6 +97,12 @@ border-radius: 0.125rem; } + &:hover { + border: 1px solid var(--color_border_bold); + box-shadow: 0 0 0 4px rgba(0,0,0,.12); + transition: all .2s ease-out; + } + @media #{$breakpoint-dimension-sm} { margin-top: 40px; } @@ -114,19 +145,3 @@ padding: 2rem 15px; } } - -.downloadButton { - background: var(--color_bg_layer); - border-radius: 0.25rem; - outline: none; - border: none; - - height: 2rem; - width: 2rem; - - &:focus-visible { - outline: solid 1px var(--color_border_focus); - outline-offset: 0.125rem; - border-radius: 0.125rem; - } -} diff --git a/src/features/home/components/home-page/home-page.component.tsx b/src/features/home/components/home-page/home-page.component.tsx index ab9f907b..eb24a20f 100644 --- a/src/features/home/components/home-page/home-page.component.tsx +++ b/src/features/home/components/home-page/home-page.component.tsx @@ -8,7 +8,6 @@ import { import { AssetsComponent } from "@/features/home/components/assets/assets.component"; import { EbookComponent } from "@/features/common/components/ebook/ebook.component"; import { StructuredData } from "@/features/seo/components/structured-data.component"; -import { HeroModalStateValues } from "@/features/home/values/hero-modal-state.values"; import { ClaimDescriptionVisibilityValues } from "@/features/common/values/claim-description-visibility.values"; import { DebuggerWidgetComponent } from "@/features/debugger/components/debugger-widget/debugger-widget.component"; import { getAuth0Dictionary } from "@/features/localization/services/ui-language-dictionary.service"; @@ -19,8 +18,6 @@ interface HomePageComponentProps { decodedPayloadInitialTabId: string; decodedHeaderDescriptionVisibility: ClaimDescriptionVisibilityValues; decodedPayloadDescriptionVisibility: ClaimDescriptionVisibilityValues; - jwtInfoState: HeroModalStateValues; - jwtWarningState: HeroModalStateValues; // debuggerInitialMode: DebuggerModeValues; } @@ -30,8 +27,6 @@ export const HomePageComponent: React.FC = ({ decodedPayloadInitialTabId, decodedHeaderDescriptionVisibility, decodedPayloadDescriptionVisibility, - jwtInfoState, - jwtWarningState, // debuggerInitialMode, }) => { const homeDictionary = getHomeDictionary(languageCode); @@ -133,15 +128,7 @@ export const HomePageComponent: React.FC = ({ /> = ({ dictionary={auth0Dictionary.ebook} /> - {/**/} diff --git a/src/features/home/config/home.config.ts b/src/features/home/config/home.config.ts deleted file mode 100644 index 7aa5eaf4..00000000 --- a/src/features/home/config/home.config.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const JWT_INFO_STATE_KEY = "jwt-info-state"; -export const JWT_WARNING_STATE_KEY = "jwt-warning-state"; diff --git a/src/features/home/values/hero-modal-state.values.ts b/src/features/home/values/hero-modal-state.values.ts deleted file mode 100644 index af1a0f15..00000000 --- a/src/features/home/values/hero-modal-state.values.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum HeroModalStateValues { - OPEN = "open", - CLOSED = "closed", -} diff --git a/src/features/home/values/hero-modal-type.values.ts b/src/features/home/values/hero-modal-type.values.ts deleted file mode 100644 index 6d8a2167..00000000 --- a/src/features/home/values/hero-modal-type.values.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum HeroModalTypeValues { - INFO = "info", - WARNING = "warning", -} diff --git a/src/features/introduction/components/ebook-ad/ebook-ad.component.tsx b/src/features/introduction/components/ebook-ad/ebook-ad.component.tsx index 574ad88a..9a9092f1 100644 --- a/src/features/introduction/components/ebook-ad/ebook-ad.component.tsx +++ b/src/features/introduction/components/ebook-ad/ebook-ad.component.tsx @@ -8,7 +8,7 @@ type EbookAdComponentProps = { export const EbookAdComponent: React.FC = ({ copy }) => { return ( - +

{copy}

); diff --git a/src/features/introduction/components/ebook-ad/ebook-ad.module.scss b/src/features/introduction/components/ebook-ad/ebook-ad.module.scss index bcb519a5..21e2db10 100644 --- a/src/features/introduction/components/ebook-ad/ebook-ad.module.scss +++ b/src/features/introduction/components/ebook-ad/ebook-ad.module.scss @@ -1,23 +1,29 @@ @use "@/libs/theme/styles/variables" as *; @use "@/libs/theme/styles/mixins" as *; +.container { + margin-bottom: 1.5rem; +} + .content { @include InnerContentFlex; position: relative; max-width: $article-max-width; - - background: linear-gradient(290deg, #f4d -18.75%, #3f59e4 98.79%); - - border-radius: 0.5rem; padding: 1rem; - margin: 1rem auto 2rem auto; - color: $Neutral0; - font-size: 1rem; - line-height: 1.25rem; + border-radius: 1rem; + border: 1px solid var(--color_border_default); + box-shadow: + 0 0 0 0.5px rgba(0, 0, 0, 0.12), + 0 2px 2px -1px rgba(0, 0, 0, 0.04), + 0 4px 4px -2px rgba(0, 0, 0, 0.04), + 0 6px 6px -3px rgba(0, 0, 0, 0.04); } .copy { font-weight: 400; + line-height: 1.25rem; + font-size: 0.875rem; + color: var(--color_fg_default); & strong { font-weight: 500; diff --git a/src/features/introduction/components/introduction-article/introduction-article.component.tsx b/src/features/introduction/components/introduction-article/introduction-article.component.tsx index e121b5e0..6d14b06a 100644 --- a/src/features/introduction/components/introduction-article/introduction-article.component.tsx +++ b/src/features/introduction/components/introduction-article/introduction-article.component.tsx @@ -5,20 +5,30 @@ import { getIntroductionContent } from "@/features/localization/services/ui-lang import { ArticleComponent } from "@/features/common/components/article/article.component"; import styles from "./introduction-article.module.scss"; import { SidebarNavComponent } from "../sidebar-nav/sidebar-nav.component"; +import { EbookAdComponent } from "../ebook-ad/ebook-ad.component"; +import { getComponentDictionary } from "@/features/localization/services/component-dictionary.service"; +import { IntroductionDictionaryModel } from "@/features/localization/models/introduction-dictionary.model"; +import { IntroductionHeroComponent } from "../introduction-hero/introduction-hero.component"; interface IntroductionArticleComponentProps { languageCode: string; + introductionDictionary: IntroductionDictionaryModel; } export const IntroductionArticleComponent: React.FC< IntroductionArticleComponentProps -> = ({ languageCode }) => { +> = ({ languageCode, introductionDictionary }) => { const Introduction = getIntroductionContent({ languageCode }); + const componentDictionary = getComponentDictionary(languageCode); return (
- - {Introduction} + +
+ + + {Introduction} +
); }; diff --git a/src/features/introduction/components/introduction-article/introduction-article.module.scss b/src/features/introduction/components/introduction-article/introduction-article.module.scss index 3c8d256f..6fd1e2ce 100644 --- a/src/features/introduction/components/introduction-article/introduction-article.module.scss +++ b/src/features/introduction/components/introduction-article/introduction-article.module.scss @@ -1,11 +1,24 @@ @use "@/libs/theme/styles/variables" as *; +@use "@/libs/theme/styles/mixins" as *; .container { - min-height: 100vh; - display: flex; - flex-direction: column; - - @media #{$breakpoint-dimension-sm} { - flex-direction: row; - } + @include InnerContentFlex; + justify-content: start; + margin-bottom: 5rem; } + +.title { + color: var(--color_fg_bold); + font-size: 2rem; + line-height: 2.5rem; + font-weight: 400; + letter-spacing: -0.96px; + + @media #{$breakpoint-dimension-xs} { + font-size: 2.5rem; + } + + @media #{$breakpoint-dimension-sm} { + font-size: 3rem; + } +} \ No newline at end of file diff --git a/src/features/introduction/components/introduction-hero/introduction-hero.module.scss b/src/features/introduction/components/introduction-hero/introduction-hero.module.scss index 02baf3c4..1f12e8b2 100644 --- a/src/features/introduction/components/introduction-hero/introduction-hero.module.scss +++ b/src/features/introduction/components/introduction-hero/introduction-hero.module.scss @@ -3,32 +3,23 @@ .container { @include Container; - - background: var(--color_bg_page); - border-bottom: 1px solid var(--color_border_bold); + margin: 1.5rem 0rem; } .content { @include InnerContentFlex; - flex-direction: column; - align-items: center; - - padding-top: 2.5rem; - padding-bottom: 2.5rem; - - @media #{$breakpoint-dimension-xs} { - padding-top: 4.5rem; - padding-bottom: 4.5rem; - } } .title { + width: 100%; color: var(--color_fg_bold); - text-align: center; + text-align: left; font-size: 2rem; - line-height: 2.5rem; - font-weight: 400; - letter-spacing: -0.96px; + line-height: 1.15; + font-weight: 500; + letter-spacing: -.96px; + max-width: 42rem; + text-wrap-style: balance; @media #{$breakpoint-dimension-xs} { font-size: 2.5rem; diff --git a/src/features/introduction/components/introduction-page/introduction-page.component.tsx b/src/features/introduction/components/introduction-page/introduction-page.component.tsx index 7e4a65a4..17ed41fe 100644 --- a/src/features/introduction/components/introduction-page/introduction-page.component.tsx +++ b/src/features/introduction/components/introduction-page/introduction-page.component.tsx @@ -4,10 +4,7 @@ import { generateArticleStructuredData } from "@/features/seo/services/structure import { Auth0CtaComponent } from "@/features/common/components/auth0-cta/auth0-cta.component"; import { getIntroductionDictionary } from "@/features/localization/services/language-dictionary.service"; import { IntroductionArticleComponent } from "@/features/introduction/components/introduction-article/introduction-article.component"; -import { IntroductionHeroComponent } from "@/features/introduction/components/introduction-hero/introduction-hero.component"; -import { EbookAdComponent } from "@/features/introduction/components/ebook-ad/ebook-ad.component"; import { getAuth0Dictionary } from "@/features/localization/services/ui-language-dictionary.service"; -import { getComponentDictionary } from "@/features/localization/services/component-dictionary.service"; interface IntroductionPageComponentProps { languageCode: string; @@ -18,7 +15,6 @@ export const IntroductionPageComponent: React.FC< > = ({ languageCode }) => { const introductionDictionary = getIntroductionDictionary(languageCode); const auth0Dictionary = getAuth0Dictionary(languageCode); - const componentDictionary = getComponentDictionary(languageCode); return ( <> @@ -121,12 +117,10 @@ export const IntroductionPageComponent: React.FC< }), ]} /> - - - { @@ -18,9 +18,8 @@ const scrollToElementWithOffset = (id: string, offset = 0) => { }; export const SidebarNavComponent: React.FC = ({ - languageCode, + introductionDictionary, }) => { - const introductionDictionary = getIntroductionDictionary(languageCode); const headings = introductionDictionary.content.headings; const [activeId, setActiveId] = useState(null); diff --git a/src/features/introduction/components/sidebar-nav/sidebar-nav.module.scss b/src/features/introduction/components/sidebar-nav/sidebar-nav.module.scss index 9902c757..b6ed7a7b 100644 --- a/src/features/introduction/components/sidebar-nav/sidebar-nav.module.scss +++ b/src/features/introduction/components/sidebar-nav/sidebar-nav.module.scss @@ -1,33 +1,35 @@ @use "@/libs/theme/styles/variables" as *; .container { - display:none; - @media #{$breakpoint-dimension-sm} { - display: inline-block; - height: 100vh; - position: sticky; - color: var(--color_fg_link); - flex-shrink: 0; - top: 0; - padding: 160px 30px 0px; - margin-bottom: 100px; - border-right: 1px solid var(--color_border_bold); - max-width: 250px; - overflow-y: auto; - } + display: none; + + @media #{$breakpoint-dimension-sm} { + display: block; + flex-shrink: 1; + position: sticky; + color: var(--color_fg_link); + margin: 2rem 0rem;; + padding: 0rem 1rem; + top: $main-nav-height + 2rem; // additional value must be equal to top margin + top padding + border-right: 1px solid var(--color_border_bold); + font-size: 0.875rem; + max-width: 20rem; + height: fit-content; + } } .title { - margin-bottom: 36px; - padding-right: 8px; - cursor: pointer; + cursor: pointer; } .title__active { - color: var(--color_fg_selected); - border-bottom: 1px solid var(--color_border_selected); + color: var(--color_fg_selected); + text-decoration: underline; } .list { - list-style-type: none; + list-style-type: none; + display: flex; + flex-direction: column; + gap: 2rem; } diff --git a/src/features/libraries/components/check-mark.component.tsx b/src/features/libraries/components/check-mark.component.tsx index 9c2b96cd..3a35d09e 100644 --- a/src/features/libraries/components/check-mark.component.tsx +++ b/src/features/libraries/components/check-mark.component.tsx @@ -1,25 +1,23 @@ import React from "react"; +import styles from "./check-mark.module.scss"; type CheckMarkComponentProps = {}; export const CheckMarkComponent: React.FC = () => { return ( - + ); diff --git a/src/features/libraries/components/check-mark.module.scss b/src/features/libraries/components/check-mark.module.scss new file mode 100644 index 00000000..b60d1611 --- /dev/null +++ b/src/features/libraries/components/check-mark.module.scss @@ -0,0 +1,5 @@ +@use "@/libs/theme/styles/variables" as *; + +.check { + color: var(--color_bg_state_success); +} \ No newline at end of file diff --git a/src/features/libraries/components/library-card/library-card..module.scss b/src/features/libraries/components/library-card/library-card..module.scss index 53e284ab..5eb5ca76 100644 --- a/src/features/libraries/components/library-card/library-card..module.scss +++ b/src/features/libraries/components/library-card/library-card..module.scss @@ -4,25 +4,27 @@ grid-column: span 1; display: flex; flex-direction: column; - - border-radius: 1rem; - border: 1px solid var(--color_border_bold); + background-color: var(--color_bg_layer); + border-radius: 1.5rem; + border: 1px solid var(--color_border_default); overflow: hidden; + transition: all .3s ease-out; } .header { display: flex; align-items: center; - gap: 1rem; - padding: 1rem 1.5rem; - color: var(--color_fg_bold); - - font-size: 1rem; + gap: .5rem; + padding: 1rem; font-style: normal; - line-height: 1.25rem; - border-bottom: 1px solid var(--color_border_bold); - background: var(--color_bg_layer_alternate); + span { + color: var(--color_fg_bold); + font-size: .875rem; + font-family: var(--font-primary); + line-height: 1.35; + font-weight: 700; + } } .logo { @@ -34,20 +36,16 @@ .content { display: grid; - grid-template-columns: repeat(2, 1fr); - padding: 1.5rem; - border-bottom: 1px solid var(--color_border_bold); - background: var(--color_bg_layer_alternate); + grid-template-columns: repeat(2,1fr); + padding: 1.5rem 1.5rem 2.5rem; } .minVersion { - grid-column: 1 / -1; - background-color: var(--color_bg_state_caution_subtle); + grid-column: 1/-1; color: var(--color_fg_on_state_caution_subtle); - padding: 0.5rem 1.5rem; - text-transform: uppercase; - font-size: 0.75rem; - letter-spacing: 0.2px; + padding: 1rem; + font-size: .875rem; + letter-spacing: .2px; display: flex; justify-content: space-between; } @@ -63,47 +61,52 @@ display: flex; flex-direction: column; list-style-type: none; - gap: 0.75rem; + gap: 0.5rem; + + & svg { + width: 1.25rem; + height: 1.25rem; + } & li { - height: 1.5rem; + height: 1.25rem; code { - padding: 1px 0.125rem; - border-radius: 0.125rem; - color: var(--color_code_dark_blue); - background-color: var(--color_bg_layer_bold); - font-size: 0.875rem; + padding: .125rem .25rem; + border-radius: .5rem; + color: var(--color_fg_bold); + background-color: var(--color_bg_layer_alternate); + box-shadow: 0 0 0 .5px rgba(0,0,0,.12),0 2px 2px -1px rgba(0,0,0,.04),0 4px 4px -2px rgba(0,0,0,.04); + font-size: .75rem; font-style: normal; font-weight: 500; - line-height: 1.25rem; /* 142.857% */ + line-height: 1.5; } } } -.listItem { +.listItem, .metadata { display: flex; align-items: center; - gap: 0.5rem; color: var(--color_fg_default); + font-size: .875rem; + font-weight: 500; +} + +.listItem { + gap: 0.5rem; } .metadata { - display: flex; - align-items: center; justify-content: space-between; - padding: 1.25rem 1.5rem; - - border-bottom: 1px solid var(--color_border_bold); + padding: .5rem; + width: calc(100% - 1rem); + margin: 0 auto; + border-radius: .5rem; background: var(--color_bg_layer_alternate); - - color: var(--color_fg_default); - font-size: 0.75rem; font-style: normal; - font-weight: 500; line-height: 140%; - letter-spacing: 0.18px; - white-space: nowrap; + letter-spacing: .18px; svg { path { @@ -125,7 +128,7 @@ .repo { display: flex; - gap: 1rem; + gap: 0.5rem; } .repo__stars { @@ -139,23 +142,24 @@ display: flex; align-items: center; line-height: 0; - gap: 0.25rem; + padding: .25rem .5rem; + border-radius: 9999px; + transition: all .2s ease-out; } .command { - flex: 1; display: flex; flex-direction: column; - padding: 1.5rem; - - background: var(--color_bg_code-editor); - color: var(--color_code_inverse); - text-wrap: wrap; - overflow: hidden; - word-break: break-word; - - font-size: 0.75rem; - font-style: normal; - font-weight: 500; - line-height: 1.25rem; + padding: 1rem 1.5rem; + border-bottom: 1px solid var(--color_border_bold); + p { + color: var(--color_fg_bold); + text-wrap: wrap; + overflow: hidden; + word-break: break-word; + font-size: .75rem; + font-style: normal; + font-weight: 300; + line-height: 1.5; + } } diff --git a/src/features/libraries/components/library-card/library-card.component.tsx b/src/features/libraries/components/library-card/library-card.component.tsx index 319bb988..80005413 100644 --- a/src/features/libraries/components/library-card/library-card.component.tsx +++ b/src/features/libraries/components/library-card/library-card.component.tsx @@ -117,6 +117,48 @@ export const LibraryCardComponent: React.FC = ({ {library.gitHubRepoPath || library.altRepoPath || name} +
+
+ {authorUrl ? ( + +
+ + {authorName} +
+ + ) : ( +
+
+ + {authorName} +
+
+ )} +
+ {support && ( +
+ + {stars} +
+ )} + + + {dictionary.viewRepo.label} + +
+
+
+ {command}
    @@ -187,47 +229,8 @@ export const LibraryCardComponent: React.FC = ({
-
- {authorUrl ? ( - -
- - {authorName} -
- - ) : ( -
-
- - {authorName} -
-
- )} -
- {support && ( -
- - {stars} -
- )} - - - {dictionary.viewRepo.label} - -
-
{minimumVersion && ( -
+
{`${dictionary.minimumVersion.label} ${minimumVersion}`} = ({
)} -
- {command} -
); }; diff --git a/src/features/libraries/components/library-hero/library-hero.component.tsx b/src/features/libraries/components/library-hero/library-hero.component.tsx index 1fda944e..c91d8424 100644 --- a/src/features/libraries/components/library-hero/library-hero.component.tsx +++ b/src/features/libraries/components/library-hero/library-hero.component.tsx @@ -64,7 +64,7 @@ export const LibraryHeroComponent: React.FC = ({ const options = useMemo(() => { return [ { - label: "ProgrammingLanguage", + label: "Language", options: [ { value: dictionary.filterPicker.defaultValue.value, diff --git a/src/features/libraries/components/library-hero/library-hero.module.scss b/src/features/libraries/components/library-hero/library-hero.module.scss index dbff04ba..01153e56 100644 --- a/src/features/libraries/components/library-hero/library-hero.module.scss +++ b/src/features/libraries/components/library-hero/library-hero.module.scss @@ -44,16 +44,15 @@ .heroTitle { color: var(--color_fg_bold); - font-size: 2rem; font-style: normal; - font-weight: 400; - line-height: 2.5rem; + font-weight: 500; + line-height: 1.15; letter-spacing: -0.8px; @media #{$breakpoint-dimension-xs} { font-size: 2.5rem; - line-height: 3rem; + line-height: 1.15; } } @@ -65,9 +64,10 @@ .label { color: var(--color_fg_bold); - font-size: 1rem; + font-size: .875rem; + font-weight: 700; line-height: 1.5rem; - letter-spacing: 0.1px; + letter-spacing: .1px; } .select__wrapper { diff --git a/src/features/libraries/components/question-mark.component.tsx b/src/features/libraries/components/question-mark.component.tsx index 9e27648e..2c238025 100644 --- a/src/features/libraries/components/question-mark.component.tsx +++ b/src/features/libraries/components/question-mark.component.tsx @@ -9,13 +9,12 @@ export const QuestionMarkComponent: React.FC = () => { width="24" height="24" viewBox="0 0 24 24" - fill="none" + fill="currentColor" > ); diff --git a/src/features/libraries/components/x-mark.component.tsx b/src/features/libraries/components/x-mark.component.tsx index dfddcc80..c72312df 100644 --- a/src/features/libraries/components/x-mark.component.tsx +++ b/src/features/libraries/components/x-mark.component.tsx @@ -1,25 +1,25 @@ import React from "react"; +import styles from "./x-mark.module.scss" type CheckMarkComponentProps = {}; export const XMarkComponent: React.FC = () => { return ( ); diff --git a/src/features/libraries/components/x-mark.module.scss b/src/features/libraries/components/x-mark.module.scss new file mode 100644 index 00000000..4ea752ab --- /dev/null +++ b/src/features/libraries/components/x-mark.module.scss @@ -0,0 +1,5 @@ +@use "@/libs/theme/styles/variables" as *; + +.mark { + color: var(--color_bg_state_danger); +} \ No newline at end of file diff --git a/src/features/localization/dictionaries/home/en.ts b/src/features/localization/dictionaries/home/en.ts index 8e437dbe..236f3cbc 100644 --- a/src/features/localization/dictionaries/home/en.ts +++ b/src/features/localization/dictionaries/home/en.ts @@ -54,17 +54,8 @@ export const enHomeDictionary: HomeDictionaryModel = { description: null, }, info: { - summary: "What is a JWT?", description: "Decode, verify, and generate JSON Web Tokens, which are an open, industry standard RFC 7519 method for representing claims securely between two parties.", - ctaButton: { - label: "Learn more about JWT", - path: sitePaths.introduction, - }, - secondaryCtaButton: { - label: "See JWT libraries", - path: sitePaths.libraries, - }, resources: { spec: { name: "RFC 7519", @@ -72,13 +63,6 @@ export const enHomeDictionary: HomeDictionaryModel = { }, }, }, - warning: { - summary: "Warning about using JWTs", - title: - "For your protection, all JWT debugging and validation happens in the browser.", - description: - "Be careful where you paste or share JWTs as they can represent credentials that grant access to resources. This site does not store or transmit your JSON Web Tokens outside of the browser.", - }, decoder: { title: "JWT Decoder", compactTitle: "Decoder", @@ -92,11 +76,12 @@ export const enHomeDictionary: HomeDictionaryModel = { label: "JWT example generator", }, jwtEditor: { - headline: "Encoded value", + headline: "Encoded Token", label: "JWT editor", title: "JSON Web Token (JWT)", compactTitle: "JWT", successMessage: "Valid JWT", + autoFocusLabel: "Enable auto-focus", }, decodedHeader: { title: "Decoded Header", @@ -105,7 +90,7 @@ export const enHomeDictionary: HomeDictionaryModel = { label: "JSON", }, claims: { - label: "Claims Table", + label: "Claims Breakdown", }, }, }, @@ -116,7 +101,7 @@ export const enHomeDictionary: HomeDictionaryModel = { label: "JSON", }, claims: { - label: "Claims Table", + label: "Claims Breakdown", }, }, }, @@ -157,18 +142,18 @@ export const enHomeDictionary: HomeDictionaryModel = { label: "Header, payload, and signature example generator", }, headerEditor: { - title: "Header: Algorithm & Token Type", + title: "Algorithm & Token Type", compactTitle: "Header", successMessage: "Valid header", }, payloadEditor: { - title: "Payload: Data", + title: "Data", compactTitle: "Payload", successMessage: "Valid payload", }, signatureEditor: { title: { - secret: "Sign JWT: Secret", + secret: "Secret", privateKey: "Sign JWT: Private Key", }, compactTitle: { @@ -185,7 +170,8 @@ export const enHomeDictionary: HomeDictionaryModel = { }, }, encodedJwt: { - title: "JSON Web Token", + title: "Encoded JWT", + heading: "JWT Signature", }, }, }; diff --git a/src/features/localization/dictionaries/home/ja.ts b/src/features/localization/dictionaries/home/ja.ts index d75fe505..c93d254c 100644 --- a/src/features/localization/dictionaries/home/ja.ts +++ b/src/features/localization/dictionaries/home/ja.ts @@ -54,17 +54,8 @@ export const jaHomeDictionary: HomeDictionaryModel = { description: null, }, info: { - summary: "JWTとは?", description: "JSON Web Tokenをデコード、検証、生成します。JSON Web Tokenは、2つの当事者間でクレームを安全に表現するための、オープンな業界標準(RFC 7519)です。", - ctaButton: { - label: "JWTの詳細情報", - path: sitePaths.introduction, - }, - secondaryCtaButton: { - label: "JWTライブラリを見る", - path: sitePaths.libraries, - }, resources: { spec: { name: "RFC 7519", @@ -72,13 +63,6 @@ export const jaHomeDictionary: HomeDictionaryModel = { }, }, }, - warning: { - summary: "JWTを使用する際の注意事項", - title: - "セキュリティを確保するため、すべてのJWTのデバッグと検証はブラウザ内で行われます。", - description: - "JWTはリソースへのアクセス権を示す認証情報として機能する可能性があるため、貼り付けや共有を行う際には十分注意してください。このサイトでは、JSON Web Tokenをブラウザの外部に保存したり送信したりすることはありません。", - }, decoder: { title: "JWTデコーダー", compactTitle: "デコーダー", @@ -96,6 +80,7 @@ export const jaHomeDictionary: HomeDictionaryModel = { title: "JSON Web Token (JWT)", compactTitle: "JWT", successMessage: "有効なJWT", + autoFocusLabel: "オートフォーカスを有効にする", }, decodedHeader: { title: "デコードされたヘッダー", @@ -157,18 +142,18 @@ export const jaHomeDictionary: HomeDictionaryModel = { label: "ヘッダー、ペイロード、署名サンプルジェネレーター", }, headerEditor: { - title: "ヘッダー:アルゴリズムとトークンタイプ", + title: "アルゴリズムとトークンタイプ", compactTitle: "ヘッダー", successMessage: "有効なヘッダー", }, payloadEditor: { - title: "ペイロード:データ", + title: "データ", compactTitle: "ペイロード", successMessage: "有効なペイロード", }, signatureEditor: { title: { - secret: "JWTに署名:シークレット", + secret: "シークレット", privateKey: "JWTに署名:秘密鍵", }, compactTitle: { diff --git a/src/features/localization/dictionaries/images/en.tsx b/src/features/localization/dictionaries/images/en.tsx index 3c571f8d..e7829e9d 100644 --- a/src/features/localization/dictionaries/images/en.tsx +++ b/src/features/localization/dictionaries/images/en.tsx @@ -1,20 +1,32 @@ -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 "../../models/brand-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 = { + alertMessage: "SVG copied to clipboard", + tooltip: "Right-click or long-press for logo options", + menu: { + brand: { + label: "Brand", + svg: { + copyLabel: "Copy Logo SVG", + downloadLabel: "Download Logo", + }, + symbol: { + copyLabel: "Copy Symbol SVG", + downloadLabel: "Download Symbol", + }, + wordmark: { + copyLabel: "Copy Wordmark SVG", + downloadLabel: "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: [ + { label: "Passkeys Playground", url: "https://learnpasskeys.io" }, + { label: "WebAuthn Playground", url: "https://webauthn.me" }, + { label: "OIDC Playground", url: "https://openidconnect.net" }, + { label: "SAML Tool", url: "https://samltool.io" }, + ], }, }, }; diff --git a/src/features/localization/dictionaries/images/ja.tsx b/src/features/localization/dictionaries/images/ja.tsx index 112f37b3..ae3e3c34 100644 --- a/src/features/localization/dictionaries/images/ja.tsx +++ b/src/features/localization/dictionaries/images/ja.tsx @@ -1,20 +1,32 @@ -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 "../../models/brand-dictionary.model"; -export const jaImagesDictionary: ImagesDictionaryModel = { - logos: { - site: { - src: jwtLogo.src, - alt: "異なる色の花びらを持つ花に似たトークン。", - width: jwtLogo.width, - height: jwtLogo.height, +export const jaBrandDictionary: BrandDictionaryModel = { + alertMessage: "SVG がクリップボードにコピーされました", + tooltip: "右クリックまたは長押し (ロゴオプション)", + menu: { + brand: { + label: "ブランド", + svg: { + copyLabel: "ロゴ SVG をコピー", + downloadLabel: "ロゴをダウンロード", + }, + symbol: { + copyLabel: "シンボル SVG をコピー", + downloadLabel: "シンボルをダウンロード", + }, + wordmark: { + copyLabel: "ワードマーク SVG をコピー", + downloadLabel: "ワードマークをダウンロード", + }, }, - auth0: { - src: auth0Logo.src, - alt: "“Auth0”ロゴ。盾の中央から四方八方に伸びる星が描かれている。", - width: auth0Logo.width, - height: auth0Logo.height, + tools: { + label: "ツール", + items: [ + { label: "Passkeys Playground", url: "https://learnpasskeys.io" }, + { label: "WebAuthn Playground", url: "https://webauthn.me" }, + { label: "OIDC Playground", url: "https://openidconnect.net" }, + { label: "SAMLツール", url: "https://samltool.io" }, + ], }, }, }; diff --git a/src/features/localization/dictionaries/layout/en.tsx b/src/features/localization/dictionaries/layout/en.tsx index 1bbe8fa3..14da086d 100644 --- a/src/features/localization/dictionaries/layout/en.tsx +++ b/src/features/localization/dictionaries/layout/en.tsx @@ -39,20 +39,6 @@ export const enLayoutDictionary: LayoutDictionaryModel = { }, ], }, - languagePicker: { - button: { - ariaLabel: "Select page language", - }, - list: { - ariaLabel: "list of page languages", - }, - options: [ - { - code: "en", - label: "English", - }, - ], - }, }, header: { links: [ @@ -162,6 +148,20 @@ export const enLayoutDictionary: LayoutDictionaryModel = { }, ], }, + languagePicker: { + button: { + ariaLabel: "Select page language", + }, + list: { + ariaLabel: "list of page languages", + }, + options: [ + { + value: "en", + label: "English", + }, + ], + }, }, errors: { notFound: { @@ -187,8 +187,8 @@ export const enLayoutDictionary: LayoutDictionaryModel = { }; if (withJapanese) { - enLayoutDictionary.ribbon.languagePicker.options.push({ + enLayoutDictionary.footer.languagePicker.options.push({ label: "日本語", - code: "ja", + value: "ja", }); } diff --git a/src/features/localization/dictionaries/layout/ja.tsx b/src/features/localization/dictionaries/layout/ja.tsx index c24af750..de2f0697 100644 --- a/src/features/localization/dictionaries/layout/ja.tsx +++ b/src/features/localization/dictionaries/layout/ja.tsx @@ -39,20 +39,6 @@ export const jaLayoutDictionary: LayoutDictionaryModel = { }, ], }, - languagePicker: { - button: { - ariaLabel: "ページの言語を選択してください", - }, - list: { - ariaLabel: "ページ言語の一覧", - }, - options: [ - { - code: "en", - label: "English", - }, - ], - }, }, header: { links: [ @@ -162,6 +148,20 @@ export const jaLayoutDictionary: LayoutDictionaryModel = { }, ], }, + languagePicker: { + button: { + ariaLabel: "ページの言語を選択してください", + }, + list: { + ariaLabel: "ページ言語の一覧", + }, + options: [ + { + value: "en", + label: "English", + }, + ], + }, }, errors: { notFound: { @@ -187,8 +187,8 @@ export const jaLayoutDictionary: LayoutDictionaryModel = { }; if (withJapanese) { - jaLayoutDictionary.ribbon.languagePicker.options.push({ + jaLayoutDictionary.footer.languagePicker.options.push({ label: "日本語", - code: "ja", + value: "ja", }); } diff --git a/src/features/localization/dictionaries/ui/pickers/en.tsx b/src/features/localization/dictionaries/ui/pickers/en.tsx index b2b4f6b7..6149197e 100644 --- a/src/features/localization/dictionaries/ui/pickers/en.tsx +++ b/src/features/localization/dictionaries/ui/pickers/en.tsx @@ -9,7 +9,7 @@ export const enPickersUiDictionary: PickersUiDictionaryModel = { exampleAlgPicker: { label: "Generate example", compactLabel: "Alg", - defaultValue: "Select a signing algorithm", + defaultValue: "Select signing algorithm", description: "Generate a JWT", closeButton: { label: "Close signing algorithm list", @@ -24,4 +24,7 @@ export const enPickersUiDictionary: PickersUiDictionaryModel = { privateKeyFormatPicker: { label: "Private Key Format", }, + base64checkbox: { + label: "Base64URL Encoded", + }, }; diff --git a/src/features/localization/dictionaries/ui/pickers/ja.tsx b/src/features/localization/dictionaries/ui/pickers/ja.tsx index b756f47d..a2648cb6 100644 --- a/src/features/localization/dictionaries/ui/pickers/ja.tsx +++ b/src/features/localization/dictionaries/ui/pickers/ja.tsx @@ -24,4 +24,7 @@ export const jaPickersUiDictionary: PickersUiDictionaryModel = { privateKeyFormatPicker: { label: "秘密鍵の形式", }, + base64checkbox: { + label: "Base64URLエンコード", + }, }; diff --git a/src/features/localization/models/brand-dictionary.model.ts b/src/features/localization/models/brand-dictionary.model.ts new file mode 100644 index 00000000..4dfcce35 --- /dev/null +++ b/src/features/localization/models/brand-dictionary.model.ts @@ -0,0 +1,30 @@ +interface ToolsMenuItem { + label: string, + url: string, +} +export interface BrandMenuItem { + copyLabel: string, + downloadLabel: string, +} +interface BrandMenuSection { + label: string; + svg: BrandMenuItem, + symbol: BrandMenuItem, + wordmark: BrandMenuItem, +} + +interface ToolsMenuSection { + label: string; + items: ToolsMenuItem[]; +} + +interface BrandMenu { + brand: BrandMenuSection; + tools: ToolsMenuSection; +} + +export interface BrandDictionaryModel { + tooltip: string; + alertMessage: string; + menu: BrandMenu; +} diff --git a/src/features/localization/models/decoder-dictionary.model.ts b/src/features/localization/models/decoder-dictionary.model.ts index ade14593..0ee81ebe 100644 --- a/src/features/localization/models/decoder-dictionary.model.ts +++ b/src/features/localization/models/decoder-dictionary.model.ts @@ -12,6 +12,7 @@ export interface DecoderDictionaryModel { title: string; compactTitle: string; successMessage: string; + autoFocusLabel: string; }; exampleGenerator: { label: string; diff --git a/src/features/localization/models/encoder-dictionary.model.ts b/src/features/localization/models/encoder-dictionary.model.ts index cafec528..3dd677ef 100644 --- a/src/features/localization/models/encoder-dictionary.model.ts +++ b/src/features/localization/models/encoder-dictionary.model.ts @@ -42,5 +42,6 @@ export interface EncoderDictionaryModel { }; encodedJwt: { title: string; + heading?: string; }; } diff --git a/src/features/localization/models/home-dictionary.model.ts b/src/features/localization/models/home-dictionary.model.ts index 8b120ba9..37edaf74 100644 --- a/src/features/localization/models/home-dictionary.model.ts +++ b/src/features/localization/models/home-dictionary.model.ts @@ -2,18 +2,12 @@ import { PageMetadataModel } from "@/features/common/models/page-metadata.model" import { HeroMetadataModel } from "@/features/common/models/hero-metadata.model"; import { EncoderDictionaryModel } from "@/features/localization/models/encoder-dictionary.model"; import { DecoderDictionaryModel } from "@/features/localization/models/decoder-dictionary.model"; -import { LinkMetadataModel } from "@/features/common/models/link-metadata.model"; -import { ImageMetadataModel } from "@/features/common/models/image-metadata.model"; export interface HomeDictionaryModel { metadata: PageMetadataModel; hero: HeroMetadataModel; info: { - summary: string; description: string; - ctaButton?: LinkMetadataModel; - secondaryCtaButton?: LinkMetadataModel; - image?: ImageMetadataModel; resources: { spec: { name: string; @@ -21,11 +15,6 @@ export interface HomeDictionaryModel { }; }; }; - warning: { - summary: string; - title: string; - description: string; - }; decoder: DecoderDictionaryModel; encoder: EncoderDictionaryModel; } diff --git a/src/features/localization/models/images-dictionary.model.ts b/src/features/localization/models/images-dictionary.model.ts deleted file mode 100644 index 392e4e75..00000000 --- a/src/features/localization/models/images-dictionary.model.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { StaticImageMetadataModel } from "@/features/common/models/static-image-metadata.model"; - -export interface ImagesDictionaryModel { - logos: { - site: StaticImageMetadataModel; - auth0: StaticImageMetadataModel; - }; -} diff --git a/src/features/localization/models/layout-dictionary.model.ts b/src/features/localization/models/layout-dictionary.model.ts index df259944..3b57231a 100644 --- a/src/features/localization/models/layout-dictionary.model.ts +++ b/src/features/localization/models/layout-dictionary.model.ts @@ -26,7 +26,6 @@ export interface LayoutDictionaryModel { }; }; themePicker: RibbonPickerModel; - languagePicker: RibbonPickerModel; }; header: { links: LinkMetadataModel[]; @@ -69,6 +68,7 @@ export interface LayoutDictionaryModel { id: string; }[]; }; + languagePicker: RibbonPickerModel; }; errors: { notFound: { diff --git a/src/features/localization/models/ui/pickers-ui-dictionary.model.ts b/src/features/localization/models/ui/pickers-ui-dictionary.model.ts index f57138bc..c3c1f7fb 100644 --- a/src/features/localization/models/ui/pickers-ui-dictionary.model.ts +++ b/src/features/localization/models/ui/pickers-ui-dictionary.model.ts @@ -22,4 +22,7 @@ export interface PickersUiDictionaryModel { privateKeyFormatPicker: { label: string; }; + base64checkbox: { + label: string; + }; } diff --git a/src/features/localization/services/brand-dictionary.service.tsx b/src/features/localization/services/brand-dictionary.service.tsx new file mode 100644 index 00000000..9afc22cb --- /dev/null +++ b/src/features/localization/services/brand-dictionary.service.tsx @@ -0,0 +1,13 @@ +import { enBrandDictionary } from "@/features/localization/dictionaries/images/en"; +import { jaBrandDictionary } from "@/features/localization/dictionaries/images/ja"; +import { BrandDictionaryModel } from "../models/brand-dictionary.model"; + +const brandDictionaries: { + [index: string]: BrandDictionaryModel; +} = { + en: enBrandDictionary, + ja: jaBrandDictionary, +}; + +export const getBrandDictionary = (language: string) => + brandDictionaries[language]; diff --git a/src/features/localization/services/images-dictionary.service.tsx b/src/features/localization/services/images-dictionary.service.tsx deleted file mode 100644 index acd51002..00000000 --- a/src/features/localization/services/images-dictionary.service.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { ImagesDictionaryModel } from "@/features/localization/models/images-dictionary.model"; -import { enImagesDictionary } from "@/features/localization/dictionaries/images/en"; -import { jaImagesDictionary } from "@/features/localization/dictionaries/images/ja"; - -const imagesDictionaries: { - [index: string]: ImagesDictionaryModel; -} = { - en: enImagesDictionary, - ja: jaImagesDictionary, -}; - -export const getImageDictionary = (language: string) => - imagesDictionaries[language]; diff --git a/src/libs/testing/data-testid.dictionary.ts b/src/libs/testing/data-testid.dictionary.ts index 5285be6f..74057789 100644 --- a/src/libs/testing/data-testid.dictionary.ts +++ b/src/libs/testing/data-testid.dictionary.ts @@ -74,6 +74,7 @@ export const dataTestidDictionary = { }, encoder: { id: "encoder", + switch: 'encoder_switch', headerEditor: { id: "encoder__headerEditor", statusBar: { diff --git a/src/libs/theme/styles/_variables.scss b/src/libs/theme/styles/_variables.scss index e5a0bd15..b42bba6a 100644 --- a/src/libs/theme/styles/_variables.scss +++ b/src/libs/theme/styles/_variables.scss @@ -148,9 +148,9 @@ $gradient-app-card---resting: linear-gradient( #3f59e4 67.69% ); -$ribbon-height: 2.5rem; -$ribbon-height-mobile: 2.5rem; -$navbar-height: 3rem; +$ribbon-height: 2rem; +$ribbon-height-mobile: 2rem; +$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..5b23baa0 100644 --- a/src/libs/theme/styles/globals.scss +++ b/src/libs/theme/styles/globals.scss @@ -48,16 +48,17 @@ --color_fg_default: var(--functional-gray-200); --color_fg_link_primary: var(--cloud); - --color_bg_app_bar: rgba(17, 17, 17, 0.66); + --color_bg_app_bar: hsla(0, 0%, 95%, 0.04); + --color_bg_app_bar_border: hsla(0, 0%, 95%, 0.12); --color_bg_app_bar_plain: rgba(17, 17, 17); --color_fg_link: var(--functional-gray-50); --color_fg_link_hover: var(--functional-gray-50); --color_fg_link_pressed: var(--functional-gray-50); - --color_fg_selected: #99a7f1; - --color_border_selected: #99a7f1; - --color_border_focus: #99a7f1; + --color_fg_selected: var(--functional-gray-100); + --color_border_selected: var(--functional-gray-850); + --color_border_focus: var(--functional-gray-850); --color_bg_auth0-cta: linear-gradient(251deg, #1e1e1e 2.29%, #191919 87.03%); @@ -65,7 +66,7 @@ --color_bg_layer_bold: var(--functional-gray-700); --color_bg_layer_boldest: var(--functional-gray-50); --color_bg_layer_alternate: var(--functional-gray-850); - --color_bg_layer_alternate-bold: var(--functional-gray-800); + --color_bg_layer_alternate-bold: var(--functional-gray-850); --color_bg_layer_elevated: var(--functional-gray-900); --color_bg_layer_highlight1: var(--ocean); @@ -77,11 +78,12 @@ --color_code_blue2: #7487eb; --color_code_blue3: #7487eb; --color_code_dark_blue: #fafbfe; - --color_code_dark_blue2: #b9c3f5; + --color_code_dark_blue2: #3f59e4; --color_code_purple: #ccbcfa; - --color_code_orange: #f1b99b; + --color_code_orange: #e7834e; --color_code_inverse: #fefaf8; --color_code_teal: #3b8d7e; + --color_code_green: #52af6d; --color_code_dark_teal: #8ed2c5; --color_code_dark_neutral: #808080; @@ -113,17 +115,18 @@ --color_border_button_outline: var(--functional-gray-50); --color_fg_on_button_outline: var(--functional-gray-50); - --color_fg_code: #8ed2c5; + --color_fg_code: #fff; --color_bg_code-editor: #000; - --color_bg_state_success: #10783f; + --color_bg_state_success: #149750; --color_fg_on_state_success: #fff; --color_fg_on_state_success_subtle: #98d2b2; --color_bg_state_success_subtle: #062a16; --color_border_state_success: #67bc8e; --color_fg_strong: #99a7f1; - + + --color_bg_state_danger: #f2ac84; --color_bg_state_danger_subtle: #46110e; --color_fg_on_state_danger_subtle: #f0b7b4; --color_border_state_danger: #e89590; @@ -138,9 +141,9 @@ --color_fg_on_state_caution: #000000; --color_fg_on_state_caution_subtle: #e3c423; - --color_jwt_header: #6dc993; - --color_jwt_payload: #e5e5e5; - --color_jwt_signature: #8c9bef; + --color_jwt_header: #e27133; + --color_jwt_payload: #9472f7; + --color_jwt_signature: #8fc88a; --color_jwt_dot: #ff69e4; --color_bg_scrollbar: #6b7380; @@ -148,15 +151,16 @@ html[data-theme="light"], html[data-theme="system-light"] { - --color_jwt_header: #096256; - --color_jwt_payload: #383838; - --color_jwt_signature: #2c3ea0; - --color_jwt_dot: #cc36b1; + --color_jwt_header: #e27133; + --color_jwt_payload: #9472f7; + --color_jwt_signature: #149750; + --color_jwt_dot: #151619; --color_bg_state_info_subtle: #eef0fd; --color_fg_on_state_info_subtle: #263588; --color_border_state_info: #3f59e4; + --color_bg_state_danger: #e27133; --color_bg_state_danger_subtle: #fbeeed; --color_fg_on_state_danger_subtle: #761c17; --color_border_state_danger: #c32f26; @@ -169,18 +173,18 @@ html[data-theme="system-light"] { --color_fg_strong: #3f59e4; - --color_bg_state_success: #10783f; + --color_bg_state_success: #8fc88a; --color_fg_on_state_success: #fff; - --color_fg_on_state_success_subtle: #094726; + --color_fg_on_state_success_subtle: #149750; --color_bg_state_success_subtle: #e6f4ec; --color_border_state_success: #10783f; - --color_fg_code: #3b8d7e; + --color_fg_code: #fff; --color_bg_code-editor: #fff; --color_fg_on_button: var(--charcoal2); --color_fg_on_button_subtle: var(--functional-gray-600); - --color_fg_on_button_primary: var(--functional-gray-0); + --color_fg_on_button_primary: var(--functional-gray-1000); --color_bg_button: var(--functional-gray-0); --color_bg_button_hover: var(--functional-gray-150); --color_bg_button_pressed: #e8e8e8; @@ -193,23 +197,24 @@ html[data-theme="system-light"] { --color_border_button_outline: var(--functional-gray-550); --color_fg_on_button_outline: var(--charcoal2); - --color_bg_page: var(--functional-gray-0); + --color_bg_page: var(--functional-gray-100); --color_fg_bold: var(--charcoal2); - --color_fg_default: var(--functional-gray-650); + --color_fg_default: var(--functional-gray-500); --color_fg_link_primary: var(--sky); - --color_bg_app_bar: rgba(241, 241, 241, 0.66); + --color_bg_app_bar: hsla(0, 0%, 7%, 0.04); --color_bg_app_bar_plain: rgba(241, 241, 241); + --color_bg_app_bar_border: hsla(0, 0%, 7%, 0.12); --color_fg_link: var(--charcoal2); --color_fg_link_hover: var(--charcoal2); --color_fg_link_pressed: var(--charcoal2); - --color_fg_selected: #3f59e4; - --color_border_selected: #3f59e4; - --color_border_focus: #3f59e4; + --color_fg_selected: var(--functional-gray-850); + --color_border_selected: var(--functional-gray-850); + --color_border_focus: var(--functional-gray-850); - --color_bg_auth0-cta: var(--functional-gray-150); + --color_bg_auth0-cta: var(--functional-gray-950); --color_bg_layer: var(--functional-gray-0); --color_bg_layer_bold: var(--functional-gray-250); @@ -223,6 +228,8 @@ html[data-theme="system-light"] { --color_bg_layer_highlight3: var(--violet); --color_bg_layer_highlight4: #a35125; + --color_button_focus_inverse: hsla(0, 0%, 100%, .24); + --color_border_default: #e8e8e8; --color_border_bold: var(--functional-gray-250); --color_border_inverse_static: var(--functional-gray-550); @@ -230,6 +237,7 @@ html[data-theme="system-light"] { --color_bg_disabled: var(--functional-gray-150); --color_fg_disabled: var(--functional-gray-500); + --color_fg_default: var(--functional-gray-500); --color_bg_input: var(--functional-gray-0); --color_fg_input_placeholder: var(--functional-gray-500); @@ -241,9 +249,9 @@ html[data-theme="system-light"] { --color_code_blue2: #7487eb; --color_code_blue3: #7487eb; --color_code_dark_blue: #10163a; - --color_code_dark_blue2: #263588; + --color_code_dark_blue2: #3f59e4; --color_code_purple: #9979f5; - --color_code_orange: #a35125; + --color_code_orange: #e7834e; --color_code_inverse: #291409; --color_code_teal: #3b8d7e; --color_code_dark_teal: #419d8c; @@ -416,15 +424,18 @@ pre[class*="language-"] { .token.property, .token.tag, -.token.number, .token.constant, .token.symbol, .token.deleted { - color: var(--color_code_orange); + color: var(--color_code_dark_blue); +} + +.token.number { + color: var(--color_code_dark_blue2) } .token.boolean { - color: var(--color_fg_code); + color: var(--color_code_orange); } .token.selector, @@ -433,7 +444,7 @@ pre[class*="language-"] { .token.char, .token.builtin, .token.inserted { - color: var(--color_code_dark_blue); + color: var(--color_code_green); } .token.operator, @@ -503,19 +514,24 @@ $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; } .react-select__control { background-color: transparent !important; border-color: var(--color_border_button) !important; + background-color: var(--color_bg_layer); + border: 1px solid var(--color_border_default) !important; height: 100% !important; - border: 1px solid var(--color_border_inverse_static) !important; + &:hover { + border-color: var(--color_border_bold) !important; + transition: 0.2s ease-out !important; + } } .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 { @@ -537,20 +553,26 @@ $picker-list-offset-lg: calc(($picker-list-width-lg - $picker-width-lg) / 2); height: 100% !important; } -.react-select__menu { - background-color: var(--color_bg_layer_alternate-bold) !important; - color: var(--color_fg_bold) !important; - border: 1px solid var(--color_border_bold) !important; - font-size: 0.875rem !important; - font-family: var(--font-primary), monospace !important; - width: max-content !important; - min-width: 100% !important; +.react-select__menu, +.language-select__menu { + background-color: var(--color_bg_layer) !important; + border-radius: 0.75rem !important; + box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 0px 1px, rgba(0, 0, 0, 0.1) 0px 4px 11px !important; + margin-bottom: 8px !important; + margin-top: 8px !important; + box-sizing: border-box !important; + border: 1px solid var(--color_border_default); + padding: 0.25rem; + overflow: hidden; + width: max-content !important; + min-width: 100% !important; } .react-select__menu-portal { } -.react-select__menu-list { +.react-select__menu-list, +.language-select__menu-list { font-family: var(--font-primary), monospace !important; padding: 0 !important; @@ -576,32 +598,35 @@ $picker-list-offset-lg: calc(($picker-list-width-lg - $picker-width-lg) / 2); } } -.react-select__option { - display: flex !important; - align-items: center !important; - color: var(--color_fg_default) !important; - position: relative !important; - height: 2.5rem !important; - border-bottom: 1px solid var(--color_border_bold) !important; - padding: 0.25rem 1rem !important; - background-color: transparent !important; - font-family: var(--font-primary), monospace !important; - - gap: 0.5rem !important; - font-size: 0.8125rem !important; - line-height: 1.3125rem !important; - letter-spacing: -0.05px !important; - - &:last-child { - border-bottom: unset; - } - - &:hover { - cursor: pointer; - } +.react-select__option, +.language-select__option { + cursor: pointer !important; + display: flex !important; + font-size: 0.875rem !important; + width: 100% !important; + user-select: none !important; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important; + background-color: transparent !important; + color: var(--color_fg_default) !important; + padding: 8px 12px 8px 1.25rem !important; + box-sizing: border-box !important; + -webkit-box-align: center !important; + align-items: center !important; + position: relative !important; + height: 1.5rem !important; + border-radius: 0.5rem !important; + font-family: var(--font-primary),monospace !important; + line-height: 1.3125rem !important; + letter-spacing: -0.05px !important; + font-weight: 500 !important; + opacity: 1 !important; + &:hover { + background-color: var(--color_bg_layer_alternate) !important; + } } -.react-select__option--is-selected { +.react-select__option--is-selected, +.language-select__option--is-selected { color: var(--color_fg_bold) !important; font-weight: 500; @@ -616,7 +641,7 @@ $picker-list-offset-lg: calc(($picker-list-width-lg - $picker-width-lg) / 2); transform: translateY(-50%); height: 0.375rem; width: 0.375rem; - background-color: var(--color_fg_selected); + background-color: var(--color_fg_bold); border-radius: 50%; } } @@ -624,7 +649,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; } diff --git a/tsconfig.json b/tsconfig.json index b1701cf6..6930c019 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,7 +20,8 @@ } ], "paths": { - "@/*": ["./src/*"] + "@/*": ["./src/*"], + "@/public/*": ["./public/*"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], diff --git a/types/svg.d.ts b/types/svg.d.ts new file mode 100644 index 00000000..c284f7d6 --- /dev/null +++ b/types/svg.d.ts @@ -0,0 +1,4 @@ +declare module "*.svg?raw" { + const content: string; + export default content; +}