From b90506a4761083deb104b4548f5a9a20042b1f37 Mon Sep 17 00:00:00 2001 From: "@lukin" Date: Sun, 15 Jan 2023 00:00:00 +0400 Subject: [PATCH] refactor!: update components --- README.md | 20 ++- .../icons/arrow-top-right-on-square.ftl | 7 + .../login/assets/icons/chevron-down.ftl | 6 + .../providers}/bitbucket.ftl | 0 .../providers}/facebook.ftl | 0 .../provider => assets/providers}/github.ftl | 0 .../provider => assets/providers}/gitlab.ftl | 0 .../provider => assets/providers}/google.ftl | 0 .../providers}/instagram.ftl | 0 .../providers}/linkedin.ftl | 0 .../providers}/microsoft.ftl | 0 .../provider => assets/providers}/oidc.ftl | 0 .../providers}/openshift.ftl | 0 .../provider => assets/providers}/paypal.ftl | 0 .../providers/providers.ftl} | 26 +-- .../providers}/stackoverflow.ftl | 0 .../provider => assets/providers}/twitter.ftl | 0 .../keywind/login/components/atoms/alert.ftl | 22 +++ theme/keywind/login/components/atoms/body.ftl | 5 + .../login/components/atoms/button-group.ftl | 5 + .../keywind/login/components/atoms/button.ftl | 33 ++++ theme/keywind/login/components/atoms/card.ftl | 19 +++ .../primary.ftl => atoms/checkbox.ftl} | 8 +- .../login/components/atoms/container.ftl | 5 + theme/keywind/login/components/atoms/form.ftl | 11 ++ .../login/components/atoms/heading.ftl | 5 + .../keywind/login/components/atoms/input.ftl | 37 +++++ theme/keywind/login/components/atoms/link.ftl | 30 ++++ .../{layout/title.ftl => atoms/logo.ftl} | 2 +- .../components/{layout => atoms}/nav.ftl | 0 .../{radio/primary.ftl => atoms/radio.ftl} | 12 +- .../login/components/button/primary.ftl | 10 -- .../login/components/button/secondary.ftl | 10 -- .../login/components/icon/chevron-down.ftl | 6 - .../login/components/icon/external-link.ftl | 6 - .../login/components/input/primary.ftl | 23 --- .../login/components/layout/alerts.ftl | 22 --- .../login/components/layout/another-way.ftl | 10 -- .../login/components/layout/card-footer.ftl | 5 - .../login/components/layout/card-header.ftl | 5 - .../login/components/layout/card-main.ftl | 5 - .../keywind/login/components/layout/card.ftl | 5 - .../login/components/layout/container.ftl | 7 - .../login/components/layout/locales.ftl | 29 ---- .../components/layout/required-fields.ftl | 3 - .../login/components/layout/subtitle.ftl | 3 - .../login/components/layout/username.ftl | 15 -- .../keywind/login/components/link/primary.ftl | 10 -- .../login/components/link/secondary.ftl | 10 -- .../molecules/identity-provider.ftl | 72 +++++++++ .../components/molecules/locale-provider.ftl | 29 ++++ .../login/components/molecules/username.ftl | 15 ++ theme/keywind/login/components/provider.ftl | 70 --------- .../login/{components => }/document.ftl | 8 +- .../labels/totp-device.ftl} | 0 .../label => features/labels}/totp.ftl | 0 .../label => features/labels}/username.ftl | 0 theme/keywind/login/login-config-totp.ftl | 114 +++++++------- .../keywind/login/login-idp-link-confirm.ftl | 23 ++- theme/keywind/login/login-oauth-grant.ftl | 21 +-- theme/keywind/login/login-otp.ftl | 63 ++++---- theme/keywind/login/login-reset-password.ftl | 52 +++--- theme/keywind/login/login-update-password.ftl | 79 ++++++---- theme/keywind/login/login-update-profile.ftl | 114 +++++++------- theme/keywind/login/login.ftl | 105 +++++++------ theme/keywind/login/logout-confirm.ftl | 23 +-- theme/keywind/login/register.ftl | 148 ++++++++---------- theme/keywind/login/resources/dist/index.css | 2 +- theme/keywind/login/template.ftl | 110 +++++++------ 69 files changed, 771 insertions(+), 714 deletions(-) create mode 100644 theme/keywind/login/assets/icons/arrow-top-right-on-square.ftl create mode 100644 theme/keywind/login/assets/icons/chevron-down.ftl rename theme/keywind/login/{components/icon/provider => assets/providers}/bitbucket.ftl (100%) rename theme/keywind/login/{components/icon/provider => assets/providers}/facebook.ftl (100%) rename theme/keywind/login/{components/icon/provider => assets/providers}/github.ftl (100%) rename theme/keywind/login/{components/icon/provider => assets/providers}/gitlab.ftl (100%) rename theme/keywind/login/{components/icon/provider => assets/providers}/google.ftl (100%) rename theme/keywind/login/{components/icon/provider => assets/providers}/instagram.ftl (100%) rename theme/keywind/login/{components/icon/provider => assets/providers}/linkedin.ftl (100%) rename theme/keywind/login/{components/icon/provider => assets/providers}/microsoft.ftl (100%) rename theme/keywind/login/{components/icon/provider => assets/providers}/oidc.ftl (100%) rename theme/keywind/login/{components/icon/provider => assets/providers}/openshift.ftl (100%) rename theme/keywind/login/{components/icon/provider => assets/providers}/paypal.ftl (100%) rename theme/keywind/login/{components/icon/provider.ftl => assets/providers/providers.ftl} (52%) rename theme/keywind/login/{components/icon/provider => assets/providers}/stackoverflow.ftl (100%) rename theme/keywind/login/{components/icon/provider => assets/providers}/twitter.ftl (100%) create mode 100644 theme/keywind/login/components/atoms/alert.ftl create mode 100644 theme/keywind/login/components/atoms/body.ftl create mode 100644 theme/keywind/login/components/atoms/button-group.ftl create mode 100644 theme/keywind/login/components/atoms/button.ftl create mode 100644 theme/keywind/login/components/atoms/card.ftl rename theme/keywind/login/components/{checkbox/primary.ftl => atoms/checkbox.ftl} (74%) create mode 100644 theme/keywind/login/components/atoms/container.ftl create mode 100644 theme/keywind/login/components/atoms/form.ftl create mode 100644 theme/keywind/login/components/atoms/heading.ftl create mode 100644 theme/keywind/login/components/atoms/input.ftl create mode 100644 theme/keywind/login/components/atoms/link.ftl rename theme/keywind/login/components/{layout/title.ftl => atoms/logo.ftl} (50%) rename theme/keywind/login/components/{layout => atoms}/nav.ftl (100%) rename theme/keywind/login/components/{radio/primary.ftl => atoms/radio.ftl} (62%) delete mode 100644 theme/keywind/login/components/button/primary.ftl delete mode 100644 theme/keywind/login/components/button/secondary.ftl delete mode 100644 theme/keywind/login/components/icon/chevron-down.ftl delete mode 100644 theme/keywind/login/components/icon/external-link.ftl delete mode 100644 theme/keywind/login/components/input/primary.ftl delete mode 100644 theme/keywind/login/components/layout/alerts.ftl delete mode 100644 theme/keywind/login/components/layout/another-way.ftl delete mode 100644 theme/keywind/login/components/layout/card-footer.ftl delete mode 100644 theme/keywind/login/components/layout/card-header.ftl delete mode 100644 theme/keywind/login/components/layout/card-main.ftl delete mode 100644 theme/keywind/login/components/layout/card.ftl delete mode 100644 theme/keywind/login/components/layout/container.ftl delete mode 100644 theme/keywind/login/components/layout/locales.ftl delete mode 100644 theme/keywind/login/components/layout/required-fields.ftl delete mode 100644 theme/keywind/login/components/layout/subtitle.ftl delete mode 100644 theme/keywind/login/components/layout/username.ftl delete mode 100644 theme/keywind/login/components/link/primary.ftl delete mode 100644 theme/keywind/login/components/link/secondary.ftl create mode 100644 theme/keywind/login/components/molecules/identity-provider.ftl create mode 100644 theme/keywind/login/components/molecules/locale-provider.ftl create mode 100644 theme/keywind/login/components/molecules/username.ftl delete mode 100644 theme/keywind/login/components/provider.ftl rename theme/keywind/login/{components => }/document.ftl (80%) rename theme/keywind/login/{components/label/userdevice.ftl => features/labels/totp-device.ftl} (100%) rename theme/keywind/login/{components/label => features/labels}/totp.ftl (100%) rename theme/keywind/login/{components/label => features/labels}/username.ftl (100%) diff --git a/README.md b/README.md index 0b59d29..d1bad4b 100644 --- a/README.md +++ b/README.md @@ -51,10 +51,11 @@ parent=keywind ### Theme -When you do need to customize a palette, you can configure your colors under the `colors` key in the `theme` section of `tailwind.config.js` file: +When you do need to customize a palette, you can configure your colors under the `colors` key in the `theme` section of Tailwind config file: + +`tailwind.config.js` ```js -// tailwind.config.js module.exports = { theme: { extend: { @@ -70,18 +71,15 @@ Read more about Tailwind CSS configuration in the [documentation](https://tailwi ### Components -You can inherit Keywind components in your own theme. For example, to resize the primary button you should create a styled `theme/mytheme/components/button/primary.ftl` file: +You can update Keywind components in your own child theme. For example, create a copy of the `body` component and change the background: + +`theme/mytheme/login/components/atoms/body.ftl` ``` -<#macro kw component="button" rest...> - <${component} - class="bg-primary-600 flex justify-center px-6 py-3 relative rounded-lg text-md text-white w-full focus:outline-none focus:ring-2 focus:ring-primary-600 focus:ring-offset-2 hover:bg-primary-700" - <#list rest as attrName, attrValue> - ${attrName}="${attrValue}" - - > +<#macro kw> + <#nested> - + ``` diff --git a/theme/keywind/login/assets/icons/arrow-top-right-on-square.ftl b/theme/keywind/login/assets/icons/arrow-top-right-on-square.ftl new file mode 100644 index 0000000..81c4bf8 --- /dev/null +++ b/theme/keywind/login/assets/icons/arrow-top-right-on-square.ftl @@ -0,0 +1,7 @@ +<#-- https://github.com/tailwindlabs/heroicons/blob/master/src/20/solid/arrow-top-right-on-square.svg --> +<#macro kw> + + + + + diff --git a/theme/keywind/login/assets/icons/chevron-down.ftl b/theme/keywind/login/assets/icons/chevron-down.ftl new file mode 100644 index 0000000..673ef11 --- /dev/null +++ b/theme/keywind/login/assets/icons/chevron-down.ftl @@ -0,0 +1,6 @@ +<#-- https://github.com/tailwindlabs/heroicons/blob/master/src/20/solid/chevron-down.svg --> +<#macro kw> + + + + diff --git a/theme/keywind/login/components/icon/provider/bitbucket.ftl b/theme/keywind/login/assets/providers/bitbucket.ftl similarity index 100% rename from theme/keywind/login/components/icon/provider/bitbucket.ftl rename to theme/keywind/login/assets/providers/bitbucket.ftl diff --git a/theme/keywind/login/components/icon/provider/facebook.ftl b/theme/keywind/login/assets/providers/facebook.ftl similarity index 100% rename from theme/keywind/login/components/icon/provider/facebook.ftl rename to theme/keywind/login/assets/providers/facebook.ftl diff --git a/theme/keywind/login/components/icon/provider/github.ftl b/theme/keywind/login/assets/providers/github.ftl similarity index 100% rename from theme/keywind/login/components/icon/provider/github.ftl rename to theme/keywind/login/assets/providers/github.ftl diff --git a/theme/keywind/login/components/icon/provider/gitlab.ftl b/theme/keywind/login/assets/providers/gitlab.ftl similarity index 100% rename from theme/keywind/login/components/icon/provider/gitlab.ftl rename to theme/keywind/login/assets/providers/gitlab.ftl diff --git a/theme/keywind/login/components/icon/provider/google.ftl b/theme/keywind/login/assets/providers/google.ftl similarity index 100% rename from theme/keywind/login/components/icon/provider/google.ftl rename to theme/keywind/login/assets/providers/google.ftl diff --git a/theme/keywind/login/components/icon/provider/instagram.ftl b/theme/keywind/login/assets/providers/instagram.ftl similarity index 100% rename from theme/keywind/login/components/icon/provider/instagram.ftl rename to theme/keywind/login/assets/providers/instagram.ftl diff --git a/theme/keywind/login/components/icon/provider/linkedin.ftl b/theme/keywind/login/assets/providers/linkedin.ftl similarity index 100% rename from theme/keywind/login/components/icon/provider/linkedin.ftl rename to theme/keywind/login/assets/providers/linkedin.ftl diff --git a/theme/keywind/login/components/icon/provider/microsoft.ftl b/theme/keywind/login/assets/providers/microsoft.ftl similarity index 100% rename from theme/keywind/login/components/icon/provider/microsoft.ftl rename to theme/keywind/login/assets/providers/microsoft.ftl diff --git a/theme/keywind/login/components/icon/provider/oidc.ftl b/theme/keywind/login/assets/providers/oidc.ftl similarity index 100% rename from theme/keywind/login/components/icon/provider/oidc.ftl rename to theme/keywind/login/assets/providers/oidc.ftl diff --git a/theme/keywind/login/components/icon/provider/openshift.ftl b/theme/keywind/login/assets/providers/openshift.ftl similarity index 100% rename from theme/keywind/login/components/icon/provider/openshift.ftl rename to theme/keywind/login/assets/providers/openshift.ftl diff --git a/theme/keywind/login/components/icon/provider/paypal.ftl b/theme/keywind/login/assets/providers/paypal.ftl similarity index 100% rename from theme/keywind/login/components/icon/provider/paypal.ftl rename to theme/keywind/login/assets/providers/paypal.ftl diff --git a/theme/keywind/login/components/icon/provider.ftl b/theme/keywind/login/assets/providers/providers.ftl similarity index 52% rename from theme/keywind/login/components/icon/provider.ftl rename to theme/keywind/login/assets/providers/providers.ftl index 165267f..fa32e52 100644 --- a/theme/keywind/login/components/icon/provider.ftl +++ b/theme/keywind/login/assets/providers/providers.ftl @@ -1,16 +1,16 @@ -<#import "./provider/bitbucket.ftl" as bitbucketIcon> -<#import "./provider/facebook.ftl" as facebookIcon> -<#import "./provider/github.ftl" as githubIcon> -<#import "./provider/gitlab.ftl" as gitlabIcon> -<#import "./provider/google.ftl" as googleIcon> -<#import "./provider/instagram.ftl" as instagramIcon> -<#import "./provider/linkedin.ftl" as linkedinIcon> -<#import "./provider/microsoft.ftl" as microsoftIcon> -<#import "./provider/oidc.ftl" as oidcIcon> -<#import "./provider/openshift.ftl" as openshiftIcon> -<#import "./provider/paypal.ftl" as paypalIcon> -<#import "./provider/stackoverflow.ftl" as stackoverflowIcon> -<#import "./provider/twitter.ftl" as twitterIcon> +<#import "./bitbucket.ftl" as bitbucketIcon> +<#import "./facebook.ftl" as facebookIcon> +<#import "./github.ftl" as githubIcon> +<#import "./gitlab.ftl" as gitlabIcon> +<#import "./google.ftl" as googleIcon> +<#import "./instagram.ftl" as instagramIcon> +<#import "./linkedin.ftl" as linkedinIcon> +<#import "./microsoft.ftl" as microsoftIcon> +<#import "./oidc.ftl" as oidcIcon> +<#import "./openshift.ftl" as openshiftIcon> +<#import "./paypal.ftl" as paypalIcon> +<#import "./stackoverflow.ftl" as stackoverflowIcon> +<#import "./twitter.ftl" as twitterIcon> <#macro bitbucket> <@bitbucketIcon.kw /> diff --git a/theme/keywind/login/components/icon/provider/stackoverflow.ftl b/theme/keywind/login/assets/providers/stackoverflow.ftl similarity index 100% rename from theme/keywind/login/components/icon/provider/stackoverflow.ftl rename to theme/keywind/login/assets/providers/stackoverflow.ftl diff --git a/theme/keywind/login/components/icon/provider/twitter.ftl b/theme/keywind/login/assets/providers/twitter.ftl similarity index 100% rename from theme/keywind/login/components/icon/provider/twitter.ftl rename to theme/keywind/login/assets/providers/twitter.ftl diff --git a/theme/keywind/login/components/atoms/alert.ftl b/theme/keywind/login/components/atoms/alert.ftl new file mode 100644 index 0000000..58e8309 --- /dev/null +++ b/theme/keywind/login/components/atoms/alert.ftl @@ -0,0 +1,22 @@ +<#macro kw color=""> + <#switch color> + <#case "error"> + <#assign colorClass="bg-red-100 text-red-600"> + <#break> + <#case "info"> + <#assign colorClass="bg-blue-100 text-blue-600"> + <#break> + <#case "success"> + <#assign colorClass="bg-green-100 text-green-600"> + <#break> + <#case "warning"> + <#assign colorClass="bg-orange-100 text-orange-600"> + <#break> + <#default> + <#assign colorClass="bg-blue-100 text-blue-600"> + + + + diff --git a/theme/keywind/login/components/atoms/body.ftl b/theme/keywind/login/components/atoms/body.ftl new file mode 100644 index 0000000..dcc94a0 --- /dev/null +++ b/theme/keywind/login/components/atoms/body.ftl @@ -0,0 +1,5 @@ +<#macro kw> + + <#nested> + + diff --git a/theme/keywind/login/components/atoms/button-group.ftl b/theme/keywind/login/components/atoms/button-group.ftl new file mode 100644 index 0000000..4591209 --- /dev/null +++ b/theme/keywind/login/components/atoms/button-group.ftl @@ -0,0 +1,5 @@ +<#macro kw> +
+ <#nested> +
+ diff --git a/theme/keywind/login/components/atoms/button.ftl b/theme/keywind/login/components/atoms/button.ftl new file mode 100644 index 0000000..eeb0af7 --- /dev/null +++ b/theme/keywind/login/components/atoms/button.ftl @@ -0,0 +1,33 @@ +<#macro kw color="" component="button" size="" rest...> + <#switch color> + <#case "primary"> + <#assign colorClass="bg-primary-600 text-white focus:ring-primary-600 hover:bg-primary-700"> + <#break> + <#case "secondary"> + <#assign colorClass="bg-secondary-100 text-secondary-600 focus:ring-secondary-600 hover:bg-secondary-200 hover:text-secondary-900"> + <#break> + <#default> + <#assign colorClass="bg-primary-600 text-white focus:ring-primary-600 hover:bg-primary-700"> + + + <#switch size> + <#case "medium"> + <#assign sizeClass="px-4 py-2 text-sm"> + <#break> + <#case "small"> + <#assign sizeClass="px-2 py-1 text-xs"> + <#break> + <#default> + <#assign sizeClass="px-4 py-2 text-sm"> + + + <${component} + class="${colorClass} ${sizeClass} flex justify-center relative rounded-lg w-full focus:outline-none focus:ring-2 focus:ring-offset-2" + + <#list rest as attrName, attrValue> + ${attrName}="${attrValue}" + + > + <#nested> + + diff --git a/theme/keywind/login/components/atoms/card.ftl b/theme/keywind/login/components/atoms/card.ftl new file mode 100644 index 0000000..c1e808d --- /dev/null +++ b/theme/keywind/login/components/atoms/card.ftl @@ -0,0 +1,19 @@ +<#macro kw content="" footer="" header=""> +
+ <#if header?has_content> +
+ ${header} +
+ + <#if content?has_content> +
+ ${content} +
+ + <#if footer?has_content> +
+ ${footer} +
+ +
+ diff --git a/theme/keywind/login/components/checkbox/primary.ftl b/theme/keywind/login/components/atoms/checkbox.ftl similarity index 74% rename from theme/keywind/login/components/checkbox/primary.ftl rename to theme/keywind/login/components/atoms/checkbox.ftl index 8f11a81..e47fd61 100644 --- a/theme/keywind/login/components/checkbox/primary.ftl +++ b/theme/keywind/login/components/atoms/checkbox.ftl @@ -1,17 +1,19 @@ -<#macro kw name checked=false rest...> +<#macro kw checked=false label="" name="" rest...>
checked + class="border-secondary-200 h-4 rounded text-primary-600 w-4 focus:ring-primary-200 focus:ring-opacity-50" id="${name}" name="${name}" type="checkbox" + <#list rest as attrName, attrValue> ${attrName}="${attrValue}" > -
diff --git a/theme/keywind/login/components/atoms/container.ftl b/theme/keywind/login/components/atoms/container.ftl new file mode 100644 index 0000000..34ead18 --- /dev/null +++ b/theme/keywind/login/components/atoms/container.ftl @@ -0,0 +1,5 @@ +<#macro kw> +
+ <#nested> +
+ diff --git a/theme/keywind/login/components/atoms/form.ftl b/theme/keywind/login/components/atoms/form.ftl new file mode 100644 index 0000000..014bb4f --- /dev/null +++ b/theme/keywind/login/components/atoms/form.ftl @@ -0,0 +1,11 @@ +<#macro kw rest...> +
+ ${attrName}="${attrValue}" + + > + <#nested> +
+ diff --git a/theme/keywind/login/components/atoms/heading.ftl b/theme/keywind/login/components/atoms/heading.ftl new file mode 100644 index 0000000..7665c01 --- /dev/null +++ b/theme/keywind/login/components/atoms/heading.ftl @@ -0,0 +1,5 @@ +<#macro kw> +

+ <#nested> +

+ diff --git a/theme/keywind/login/components/atoms/input.ftl b/theme/keywind/login/components/atoms/input.ftl new file mode 100644 index 0000000..01e2897 --- /dev/null +++ b/theme/keywind/login/components/atoms/input.ftl @@ -0,0 +1,37 @@ +<#macro + kw + autofocus=false + disabled=false + invalid=false + label="" + message="" + name="" + required=true + rest... +> +
+ + autofocus + <#if disabled>disabled + <#if required>required + + aria-invalid="${invalid?c}" + class="block border-secondary-200 mt-1 rounded-md w-full focus:border-primary-300 focus:ring focus:ring-primary-200 focus:ring-opacity-50 sm:text-sm" + id="${name}" + name="${name}" + placeholder="${label}" + + <#list rest as attrName, attrValue> + ${attrName}="${attrValue}" + + > + <#if invalid?? && message??> +
+ ${message?no_esc} +
+ +
+ diff --git a/theme/keywind/login/components/atoms/link.ftl b/theme/keywind/login/components/atoms/link.ftl new file mode 100644 index 0000000..bde7666 --- /dev/null +++ b/theme/keywind/login/components/atoms/link.ftl @@ -0,0 +1,30 @@ +<#macro kw color="" component="a" size="" rest...> + <#switch color> + <#case "primary"> + <#assign colorClass="text-primary-600 hover:text-primary-500"> + <#break> + <#case "secondary"> + <#assign colorClass="text-secondary-600 hover:text-secondary-900"> + <#break> + <#default> + <#assign colorClass="text-primary-600 hover:text-primary-500"> + + + <#switch size> + <#case "small"> + <#assign sizeClass="text-sm"> + <#break> + <#default> + <#assign sizeClass=""> + + + <${component} + class="<#compress>${colorClass} ${sizeClass} inline-flex" + + <#list rest as attrName, attrValue> + ${attrName}="${attrValue}" + + > + <#nested> + + diff --git a/theme/keywind/login/components/layout/title.ftl b/theme/keywind/login/components/atoms/logo.ftl similarity index 50% rename from theme/keywind/login/components/layout/title.ftl rename to theme/keywind/login/components/atoms/logo.ftl index ea381bf..f166403 100644 --- a/theme/keywind/login/components/layout/title.ftl +++ b/theme/keywind/login/components/atoms/logo.ftl @@ -1,5 +1,5 @@ <#macro kw>
- ${kcSanitize(msg("loginTitleHtml", (realm.displayNameHtml!"")))?no_esc} + <#nested>
diff --git a/theme/keywind/login/components/layout/nav.ftl b/theme/keywind/login/components/atoms/nav.ftl similarity index 100% rename from theme/keywind/login/components/layout/nav.ftl rename to theme/keywind/login/components/atoms/nav.ftl diff --git a/theme/keywind/login/components/radio/primary.ftl b/theme/keywind/login/components/atoms/radio.ftl similarity index 62% rename from theme/keywind/login/components/radio/primary.ftl rename to theme/keywind/login/components/atoms/radio.ftl index 0528542..5596d5c 100644 --- a/theme/keywind/login/components/radio/primary.ftl +++ b/theme/keywind/login/components/atoms/radio.ftl @@ -1,20 +1,18 @@ -<#macro kw id tabIndex checked=false rest...> +<#macro kw checked=false id="" label="" rest...>
checked + class="border-secondary-200 focus:ring-primary-600" id="${id}" type="radio" + <#list rest as attrName, attrValue> ${attrName}="${attrValue}" > -
diff --git a/theme/keywind/login/components/button/primary.ftl b/theme/keywind/login/components/button/primary.ftl deleted file mode 100644 index 4b740e6..0000000 --- a/theme/keywind/login/components/button/primary.ftl +++ /dev/null @@ -1,10 +0,0 @@ -<#macro kw component="button" rest...> - <${component} - class="bg-primary-600 flex justify-center px-4 py-2 relative rounded-lg text-sm text-white w-full focus:outline-none focus:ring-2 focus:ring-primary-600 focus:ring-offset-2 hover:bg-primary-700" - <#list rest as attrName, attrValue> - ${attrName}="${attrValue}" - - > - <#nested> - - diff --git a/theme/keywind/login/components/button/secondary.ftl b/theme/keywind/login/components/button/secondary.ftl deleted file mode 100644 index 554452e..0000000 --- a/theme/keywind/login/components/button/secondary.ftl +++ /dev/null @@ -1,10 +0,0 @@ -<#macro kw component="button" rest...> - <${component} - class="bg-secondary-100 flex justify-center px-4 py-2 relative rounded-lg text-sm text-secondary-600 w-full focus:outline-none focus:ring-2 focus:ring-secondary-600 focus:ring-offset-2 hover:bg-secondary-200 hover:text-secondary-900" - <#list rest as attrName, attrValue> - ${attrName}="${attrValue}" - - > - <#nested> - - diff --git a/theme/keywind/login/components/icon/chevron-down.ftl b/theme/keywind/login/components/icon/chevron-down.ftl deleted file mode 100644 index 9e21e8f..0000000 --- a/theme/keywind/login/components/icon/chevron-down.ftl +++ /dev/null @@ -1,6 +0,0 @@ -<#-- https://github.com/tailwindlabs/heroicons/blob/master/src/outline/chevron-down.svg --> -<#macro kw> - - - - diff --git a/theme/keywind/login/components/icon/external-link.ftl b/theme/keywind/login/components/icon/external-link.ftl deleted file mode 100644 index f18b6b2..0000000 --- a/theme/keywind/login/components/icon/external-link.ftl +++ /dev/null @@ -1,6 +0,0 @@ -<#-- https://github.com/tailwindlabs/heroicons/blob/master/src/outline/external-link.svg --> -<#macro kw> - - - - diff --git a/theme/keywind/login/components/input/primary.ftl b/theme/keywind/login/components/input/primary.ftl deleted file mode 100644 index 0ea494f..0000000 --- a/theme/keywind/login/components/input/primary.ftl +++ /dev/null @@ -1,23 +0,0 @@ -<#macro kw invalid name autofocus=false disabled=false message=true required=true rest...> - - autofocus - <#if disabled>disabled - <#if required>required - aria-invalid="${messagesPerField.existsError(invalid)?c}" - class="block border-secondary-200 mt-1 rounded-md w-full focus:border-primary-300 focus:ring focus:ring-primary-200 focus:ring-opacity-50 sm:text-sm" - id="${name}" - name="${name}" - placeholder="<#compress><#nested>" - <#list rest as attrName, attrValue> - ${attrName}="${attrValue}" - - > - <#if message && messagesPerField.existsError(invalid)> -
- ${kcSanitize(messagesPerField.getFirstError(invalid))?no_esc} -
- - diff --git a/theme/keywind/login/components/layout/alerts.ftl b/theme/keywind/login/components/layout/alerts.ftl deleted file mode 100644 index a695261..0000000 --- a/theme/keywind/login/components/layout/alerts.ftl +++ /dev/null @@ -1,22 +0,0 @@ -<#macro kw> - <#switch message.type> - <#case "error"> - <#assign color="bg-red-100 text-red-600"> - <#break> - <#case "info"> - <#assign color="bg-blue-100 text-blue-600"> - <#break> - <#case "success"> - <#assign color="bg-green-100 text-green-600"> - <#break> - <#case "warning"> - <#assign color="bg-orange-100 text-orange-600"> - <#break> - <#default> - <#assign color="bg-blue-100 text-blue-600"> - - - - diff --git a/theme/keywind/login/components/layout/another-way.ftl b/theme/keywind/login/components/layout/another-way.ftl deleted file mode 100644 index 24f464e..0000000 --- a/theme/keywind/login/components/layout/another-way.ftl +++ /dev/null @@ -1,10 +0,0 @@ -<#import "../button/primary.ftl" as buttonPrimary> - -<#macro kw> -
- - <@buttonPrimary.kw type="submit"> - ${msg("doTryAnotherWay")} - -
- diff --git a/theme/keywind/login/components/layout/card-footer.ftl b/theme/keywind/login/components/layout/card-footer.ftl deleted file mode 100644 index 4d502ff..0000000 --- a/theme/keywind/login/components/layout/card-footer.ftl +++ /dev/null @@ -1,5 +0,0 @@ -<#macro kw> - - diff --git a/theme/keywind/login/components/layout/card-header.ftl b/theme/keywind/login/components/layout/card-header.ftl deleted file mode 100644 index 4dd3be4..0000000 --- a/theme/keywind/login/components/layout/card-header.ftl +++ /dev/null @@ -1,5 +0,0 @@ -<#macro kw> -
- <#nested> -
- diff --git a/theme/keywind/login/components/layout/card-main.ftl b/theme/keywind/login/components/layout/card-main.ftl deleted file mode 100644 index 2848132..0000000 --- a/theme/keywind/login/components/layout/card-main.ftl +++ /dev/null @@ -1,5 +0,0 @@ -<#macro kw> -
- <#nested> -
- diff --git a/theme/keywind/login/components/layout/card.ftl b/theme/keywind/login/components/layout/card.ftl deleted file mode 100644 index 836fff6..0000000 --- a/theme/keywind/login/components/layout/card.ftl +++ /dev/null @@ -1,5 +0,0 @@ -<#macro kw> -
- <#nested> -
- diff --git a/theme/keywind/login/components/layout/container.ftl b/theme/keywind/login/components/layout/container.ftl deleted file mode 100644 index fdd0fe7..0000000 --- a/theme/keywind/login/components/layout/container.ftl +++ /dev/null @@ -1,7 +0,0 @@ -<#macro kw> -
-
- <#nested> -
-
- diff --git a/theme/keywind/login/components/layout/locales.ftl b/theme/keywind/login/components/layout/locales.ftl deleted file mode 100644 index 0fb4318..0000000 --- a/theme/keywind/login/components/layout/locales.ftl +++ /dev/null @@ -1,29 +0,0 @@ -<#import "../icon/chevron-down.ftl" as iconChevronDown> -<#import "../link/secondary.ftl" as linkSecondary> - -<#macro kw> -
- <@linkSecondary.kw component="button" type="button" @click="open = true"> -
- ${locale.current} - <@iconChevronDown.kw /> -
- -
- <#list locale.supported as locales> - <#if locale.current != locales.label> -
- <@linkSecondary.kw href=locales.url> - ${locales.label} - -
- - -
-
- diff --git a/theme/keywind/login/components/layout/required-fields.ftl b/theme/keywind/login/components/layout/required-fields.ftl deleted file mode 100644 index 3e34bd7..0000000 --- a/theme/keywind/login/components/layout/required-fields.ftl +++ /dev/null @@ -1,3 +0,0 @@ -<#macro kw> -
* ${msg("requiredFields")}
- diff --git a/theme/keywind/login/components/layout/subtitle.ftl b/theme/keywind/login/components/layout/subtitle.ftl deleted file mode 100644 index 984bc9c..0000000 --- a/theme/keywind/login/components/layout/subtitle.ftl +++ /dev/null @@ -1,3 +0,0 @@ -<#macro kw> -

<#nested "header">

- diff --git a/theme/keywind/login/components/layout/username.ftl b/theme/keywind/login/components/layout/username.ftl deleted file mode 100644 index 927c964..0000000 --- a/theme/keywind/login/components/layout/username.ftl +++ /dev/null @@ -1,15 +0,0 @@ -<#import "../icon/external-link.ftl" as iconExternalLink> -<#import "../link/primary.ftl" as linkPrimary> - -<#macro kw> - <#nested "show-username"> -
- ${auth.attemptedUsername} - <@linkPrimary.kw - href="${url.loginRestartFlowUrl}" - title="${msg('restartLoginTooltip')}" - > - <@iconExternalLink.kw /> - -
- diff --git a/theme/keywind/login/components/link/primary.ftl b/theme/keywind/login/components/link/primary.ftl deleted file mode 100644 index 53a9470..0000000 --- a/theme/keywind/login/components/link/primary.ftl +++ /dev/null @@ -1,10 +0,0 @@ -<#macro kw component="a" rest...> - <${component} - class="inline-flex text-primary-600 hover:text-primary-500" - <#list rest as attrName, attrValue> - ${attrName}="${attrValue}" - - > - <#nested> - - diff --git a/theme/keywind/login/components/link/secondary.ftl b/theme/keywind/login/components/link/secondary.ftl deleted file mode 100644 index a250061..0000000 --- a/theme/keywind/login/components/link/secondary.ftl +++ /dev/null @@ -1,10 +0,0 @@ -<#macro kw component="a" rest...> - <${component} - class="inline-flex text-secondary-600 hover:text-secondary-900" - <#list rest as attrName, attrValue> - ${attrName}="${attrValue}" - - > - <#nested> - - diff --git a/theme/keywind/login/components/molecules/identity-provider.ftl b/theme/keywind/login/components/molecules/identity-provider.ftl new file mode 100644 index 0000000..2781a61 --- /dev/null +++ b/theme/keywind/login/components/molecules/identity-provider.ftl @@ -0,0 +1,72 @@ +<#import "/assets/providers/providers.ftl" as providerIcons> + +<#macro kw providers=[]> +
+ ${msg("identity-provider-login-label")} +
+
+ <#list providers as provider> + <#switch provider.alias> + <#case "bitbucket"> + <#assign colorClass="hover:bg-provider-bitbucket/10"> + <#break> + <#case "facebook"> + <#assign colorClass="hover:bg-provider-facebook/10"> + <#break> + <#case "github"> + <#assign colorClass="hover:bg-provider-github/10"> + <#break> + <#case "gitlab"> + <#assign colorClass="hover:bg-provider-gitlab/10"> + <#break> + <#case "google"> + <#assign colorClass="hover:bg-provider-google/10"> + <#break> + <#case "instagram"> + <#assign colorClass="hover:bg-provider-instagram/10"> + <#break> + <#case "linkedin"> + <#assign colorClass="hover:bg-provider-linkedin/10"> + <#break> + <#case "microsoft"> + <#assign colorClass="hover:bg-provider-microsoft/10"> + <#break> + <#case "oidc"> + <#assign colorClass="hover:bg-provider-oidc/10"> + <#break> + <#case "openshift-v3"> + <#assign colorClass="hover:bg-provider-openshift/10"> + <#break> + <#case "openshift-v4"> + <#assign colorClass="hover:bg-provider-openshift/10"> + <#break> + <#case "paypal"> + <#assign colorClass="hover:bg-provider-paypal/10"> + <#break> + <#case "stackoverflow"> + <#assign colorClass="hover:bg-provider-stackoverflow/10"> + <#break> + <#case "twitter"> + <#assign colorClass="hover:bg-provider-twitter/10"> + <#break> + <#default> + <#assign colorClass="hover:bg-secondary-100"> + + + + <#if providerIcons[provider.alias]??> +
+ <@providerIcons[provider.alias] /> +
+ <#else> + ${provider.displayName!} + +
+ +
+ diff --git a/theme/keywind/login/components/molecules/locale-provider.ftl b/theme/keywind/login/components/molecules/locale-provider.ftl new file mode 100644 index 0000000..198e5be --- /dev/null +++ b/theme/keywind/login/components/molecules/locale-provider.ftl @@ -0,0 +1,29 @@ +<#import "/assets/icons/chevron-down.ftl" as icon> +<#import "/components/atoms/link.ftl" as link> + +<#macro kw currentLocale="" locales=[]> +
+ <@link.kw @click="open = true" color="secondary" component="button" type="button"> +
+ ${currentLocale} + <@icon.kw /> +
+ +
+ <#list locales as locale> + <#if currentLocale != locale.label> +
+ <@link.kw color="secondary" href=locale.url size="small"> + ${locale.label} + +
+ + +
+
+ diff --git a/theme/keywind/login/components/molecules/username.ftl b/theme/keywind/login/components/molecules/username.ftl new file mode 100644 index 0000000..0c373cb --- /dev/null +++ b/theme/keywind/login/components/molecules/username.ftl @@ -0,0 +1,15 @@ +<#import "/assets/icons/arrow-top-right-on-square.ftl" as icon> +<#import "/components/atoms/link.ftl" as link> + +<#macro kw linkHref="" linkTitle="" name=""> +
+ ${name} + <@link.kw + color="primary" + href=linkHref + title=linkTitle + > + <@icon.kw /> + +
+ diff --git a/theme/keywind/login/components/provider.ftl b/theme/keywind/login/components/provider.ftl deleted file mode 100644 index 6a34f15..0000000 --- a/theme/keywind/login/components/provider.ftl +++ /dev/null @@ -1,70 +0,0 @@ -<#import "./icon/provider.ftl" as iconProvider> - -<#macro kw> -
${msg("identity-provider-login-label")}
-
- <#list social.providers as provider> - <#switch provider.alias> - <#case "bitbucket"> - <#assign color="hover:bg-provider-bitbucket/10"> - <#break> - <#case "facebook"> - <#assign color="hover:bg-provider-facebook/10"> - <#break> - <#case "github"> - <#assign color="hover:bg-provider-github/10"> - <#break> - <#case "gitlab"> - <#assign color="hover:bg-provider-gitlab/10"> - <#break> - <#case "google"> - <#assign color="hover:bg-provider-google/10"> - <#break> - <#case "instagram"> - <#assign color="hover:bg-provider-instagram/10"> - <#break> - <#case "linkedin"> - <#assign color="hover:bg-provider-linkedin/10"> - <#break> - <#case "microsoft"> - <#assign color="hover:bg-provider-microsoft/10"> - <#break> - <#case "oidc"> - <#assign color="hover:bg-provider-oidc/10"> - <#break> - <#case "openshift-v3"> - <#assign color="hover:bg-provider-openshift/10"> - <#break> - <#case "openshift-v4"> - <#assign color="hover:bg-provider-openshift/10"> - <#break> - <#case "paypal"> - <#assign color="hover:bg-provider-paypal/10"> - <#break> - <#case "stackoverflow"> - <#assign color="hover:bg-provider-stackoverflow/10"> - <#break> - <#case "twitter"> - <#assign color="hover:bg-provider-twitter/10"> - <#break> - <#default> - <#assign color="hover:bg-secondary-100"> - - - - <#if iconProvider[provider.alias]??> -
- <@iconProvider[provider.alias] /> -
- <#else> - ${provider.displayName!} - -
- -
- diff --git a/theme/keywind/login/components/document.ftl b/theme/keywind/login/document.ftl similarity index 80% rename from theme/keywind/login/components/document.ftl rename to theme/keywind/login/document.ftl index 4ea53ff..f519da9 100644 --- a/theme/keywind/login/components/document.ftl +++ b/theme/keywind/login/document.ftl @@ -1,4 +1,4 @@ -<#macro kw> +<#macro kw script=""> ${msg("loginTitle", (realm.displayName!""))} @@ -23,9 +23,13 @@ + <#if script?has_content> + + + <#if properties.scripts?has_content> <#list properties.scripts?split(" ") as script> - + diff --git a/theme/keywind/login/components/label/userdevice.ftl b/theme/keywind/login/features/labels/totp-device.ftl similarity index 100% rename from theme/keywind/login/components/label/userdevice.ftl rename to theme/keywind/login/features/labels/totp-device.ftl diff --git a/theme/keywind/login/components/label/totp.ftl b/theme/keywind/login/features/labels/totp.ftl similarity index 100% rename from theme/keywind/login/components/label/totp.ftl rename to theme/keywind/login/features/labels/totp.ftl diff --git a/theme/keywind/login/components/label/username.ftl b/theme/keywind/login/features/labels/username.ftl similarity index 100% rename from theme/keywind/login/components/label/username.ftl rename to theme/keywind/login/features/labels/username.ftl diff --git a/theme/keywind/login/login-config-totp.ftl b/theme/keywind/login/login-config-totp.ftl index 760384a..1e57ad7 100644 --- a/theme/keywind/login/login-config-totp.ftl +++ b/theme/keywind/login/login-config-totp.ftl @@ -1,10 +1,14 @@ <#import "template.ftl" as layout> -<#import "components/button/primary.ftl" as buttonPrimary> -<#import "components/button/secondary.ftl" as buttonSecondary> -<#import "components/input/primary.ftl" as inputPrimary> -<#import "components/label/totp.ftl" as labelTotp> -<#import "components/label/userdevice.ftl" as labelUserDevice> -<#import "components/link/primary.ftl" as linkPrimary> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> +<#import "components/atoms/form.ftl" as form> +<#import "components/atoms/input.ftl" as input> +<#import "components/atoms/link.ftl" as link> +<#import "features/labels/totp.ftl" as totpLabel> +<#import "features/labels/totp-device.ftl" as totpDeviceLabel> + +<#assign totpLabel><@totpLabel.kw /> +<#assign totpDeviceLabel><@totpDeviceLabel.kw /> <@layout.registrationLayout displayMessage=!messagesPerField.existsError("totp", "userLabel") @@ -24,15 +28,15 @@ - <#if mode?? && mode = "manual"> -
  • + <#if mode?? && mode="manual"> +
  • ${msg("loginTotpManualStep2")}

    ${totp.totpSecretEncoded}

  • - <@linkPrimary.kw href=totp.qrUrl> + <@link.kw color="primary" href=totp.qrUrl> ${msg("loginTotpScanBarcode")} - +
  • ${msg("loginTotpManualStep3")}

    @@ -40,9 +44,9 @@
  • ${msg("loginTotpType")}: ${msg("loginTotp." + totp.policy.type)}
  • ${msg("loginTotpAlgorithm")}: ${totp.policy.getAlgorithmKey()}
  • ${msg("loginTotpDigits")}: ${totp.policy.digits}
  • - <#if totp.policy.type = "totp"> + <#if totp.policy.type="totp">
  • ${msg("loginTotpInterval")}: ${totp.policy.period}
  • - <#elseif totp.policy.type = "hotp"> + <#elseif totp.policy.type="hotp">
  • ${msg("loginTotpCounter")}: ${totp.policy.initialCounter}
  • @@ -55,58 +59,52 @@ class="mx-auto" src="data:image/png;base64, ${totp.totpSecretQrCode}" > - <@linkPrimary.kw href=totp.manualUrl> + <@link.kw color="primary" href=totp.manualUrl> ${msg("loginTotpUnableToScan")} - +
  • ${msg("loginTotpStep3")}
  • ${msg("loginTotpStep3DeviceName")}
  • -
    -
    - <@inputPrimary.kw - autocomplete="off" - autofocus=true - invalid=["totp"] - name="totp" - required=false - type="text" - > - <@labelTotp.kw /> - - - <#if mode??> - - -
    -
    - <@inputPrimary.kw - autocomplete="off" - invalid=["userLabel"] - name="userLabel" - required=false - type="text" - > - <@labelUserDevice.kw /> - -
    - <#if isAppInitiatedAction??> -
    - <@buttonPrimary.kw type="submit"> - ${msg("doSubmit")} - - <@buttonSecondary.kw name="cancel-aia" type="submit"> - ${msg("doCancel")} - -
    - <#else> -
    - <@buttonPrimary.kw type="submit"> - ${msg("doSubmit")} - -
    + <@form.kw action=url.loginAction method="post"> + + <#if mode??> + -
    + <@input.kw + autocomplete="off" + autofocus=true + invalid=messagesPerField.existsError("totp") + label=totpLabel + message=kcSanitize(messagesPerField.get("totp")) + name="totp" + required=false + type="text" + /> + <@input.kw + autocomplete="off" + invalid=messagesPerField.existsError("userLabel") + label=totpDeviceLabel + message=kcSanitize(messagesPerField.get("userLabel")) + name="userLabel" + required=false + type="text" + /> + <@buttonGroup.kw> + <#if isAppInitiatedAction??> + <@button.kw color="primary" type="submit"> + ${msg("doSubmit")} + + <@button.kw color="secondary" name="cancel-aia" type="submit" value="true"> + ${msg("doCancel")} + + <#else> + <@button.kw color="primary" type="submit"> + ${msg("doSubmit")} + + + + diff --git a/theme/keywind/login/login-idp-link-confirm.ftl b/theme/keywind/login/login-idp-link-confirm.ftl index 5f7923e..9a2554d 100644 --- a/theme/keywind/login/login-idp-link-confirm.ftl +++ b/theme/keywind/login/login-idp-link-confirm.ftl @@ -1,21 +1,18 @@ <#import "template.ftl" as layout> -<#import "components/button/primary.ftl" as buttonPrimary> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/form.ftl" as form> <@layout.registrationLayout; section> <#if section="header"> ${msg("confirmLinkIdpTitle")} <#elseif section="form"> -
    -
    - <@buttonPrimary.kw name="submitAction" type="submit" value="updateProfile"> - ${msg("confirmLinkIdpReviewProfile")} - -
    -
    - <@buttonPrimary.kw name="submitAction" type="submit" value="linkAccount"> - ${msg("confirmLinkIdpContinue", idpDisplayName)} - -
    -
    + <@form.kw action=url.loginAction method="post"> + <@button.kw color="primary" name="submitAction" type="submit" value="updateProfile"> + ${msg("confirmLinkIdpReviewProfile")} + + <@button.kw color="primary" name="submitAction" type="submit" value="linkAccount"> + ${msg("confirmLinkIdpContinue", idpDisplayName)} + + diff --git a/theme/keywind/login/login-oauth-grant.ftl b/theme/keywind/login/login-oauth-grant.ftl index d7fb483..aa4173c 100644 --- a/theme/keywind/login/login-oauth-grant.ftl +++ b/theme/keywind/login/login-oauth-grant.ftl @@ -1,6 +1,7 @@ <#import "template.ftl" as layout> -<#import "components/button/primary.ftl" as buttonPrimary> -<#import "components/button/secondary.ftl" as buttonSecondary> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> +<#import "components/atoms/form.ftl" as form> <@layout.registrationLayout; section> <#if section="header"> @@ -46,16 +47,16 @@ -
    + <@form.kw action=url.oauthAction method="post"> -
    - <@buttonPrimary.kw name="accept" type="submit"> + <@buttonGroup.kw> + <@button.kw color="primary" name="accept" type="submit"> ${msg("doYes")} - - <@buttonSecondary.kw name="cancel" type="submit"> + + <@button.kw color="secondary" name="cancel" type="submit"> ${msg("doNo")} - -
    -
    + + + diff --git a/theme/keywind/login/login-otp.ftl b/theme/keywind/login/login-otp.ftl index f4297a3..b1bb3b9 100644 --- a/theme/keywind/login/login-otp.ftl +++ b/theme/keywind/login/login-otp.ftl @@ -1,9 +1,12 @@ <#import "template.ftl" as layout> -<#import "components/button/primary.ftl" as buttonPrimary> -<#import "components/input/primary.ftl" as inputPrimary> -<#import "components/label/totp.ftl" as labelTotp> -<#import "components/link/secondary.ftl" as linkSecondary> -<#import "components/radio/primary.ftl" as radioPrimary> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> +<#import "components/atoms/form.ftl" as form> +<#import "components/atoms/input.ftl" as input> +<#import "components/atoms/radio.ftl" as radio> +<#import "features/labels/totp.ftl" as totpLabel> + +<#assign totpLabel><@totpLabel.kw /> <@layout.registrationLayout displayMessage=!messagesPerField.existsError("totp") @@ -13,45 +16,35 @@ <#if section="header"> ${msg("doLogIn")} <#elseif section="form"> -
    + <@form.kw action=url.loginAction method="post"> <#if otpLogin.userOtpCredentials?size gt 1>
    <#list otpLogin.userOtpCredentials as otpCredential> - <@radioPrimary.kw + <@radio.kw checked=(otpCredential.id == otpLogin.selectedCredentialId) id="kw-otp-credential-${otpCredential?index}" + label=otpCredential.userLabel name="selectedCredentialId" - tabIndex="${otpCredential?index}" - value="${otpCredential.id}" - > - ${otpCredential.userLabel} - + tabindex=otpCredential?index + value=otpCredential.id + />
    -
    - <@inputPrimary.kw - autocomplete="off" - autofocus=true - invalid=["totp"] - name="otp" - type="text" - > - <@labelTotp.kw /> - -
    -
    - <@buttonPrimary.kw - name="submitAction" - type="submit" - > + <@input.kw + autocomplete="off" + autofocus=true + invalid=messagesPerField.existsError("totp") + label=totpLabel + message=kcSanitize(messagesPerField.get("totp")) + name="otp" + type="text" + /> + <@buttonGroup.kw> + <@button.kw color="primary" name="submitAction" type="submit"> ${msg("doLogIn")} - -
    -
    + + + diff --git a/theme/keywind/login/login-reset-password.ftl b/theme/keywind/login/login-reset-password.ftl index 5aa0a1e..b0516aa 100644 --- a/theme/keywind/login/login-reset-password.ftl +++ b/theme/keywind/login/login-reset-password.ftl @@ -1,8 +1,12 @@ <#import "template.ftl" as layout> -<#import "components/button/primary.ftl" as buttonPrimary> -<#import "components/input/primary.ftl" as inputPrimary> -<#import "components/label/username.ftl" as labelUsername> -<#import "components/link/secondary.ftl" as linkSecondary> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> +<#import "components/atoms/form.ftl" as form> +<#import "components/atoms/input.ftl" as input> +<#import "components/atoms/link.ftl" as link> +<#import "features/labels/username.ftl" as usernameLabel> + +<#assign usernameLabel><@usernameLabel.kw /> <@layout.registrationLayout displayInfo=true @@ -13,30 +17,28 @@ <#if section="header"> ${msg("emailForgotTitle")} <#elseif section="form"> -
    -
    - <@inputPrimary.kw - autocomplete=realm.loginWithEmailAllowed?string("email", "username") - autofocus=true - invalid=["username"] - name="username" - type="text" - value=(auth?has_content && auth.showUsername())?then(auth.attemptedUsername, '') - > - <@labelUsername.kw /> - -
    -
    - <@buttonPrimary.kw type="submit"> + <@form.kw action=url.loginAction method="post"> + <@input.kw + autocomplete=realm.loginWithEmailAllowed?string("email", "username") + autofocus=true + invalid=messagesPerField.existsError("username") + label=usernameLabel + message=kcSanitize(messagesPerField.get("username")) + name="username" + type="text" + value=(auth?has_content && auth.showUsername())?then(auth.attemptedUsername, '') + /> + <@buttonGroup.kw> + <@button.kw color="primary" type="submit"> ${msg("doSubmit")} - -
    -
    + + + <#elseif section="info"> ${msg("emailInstruction")} <#elseif section="nav"> - <@linkSecondary.kw href=url.loginUrl> - ${kcSanitize(msg("backToLogin"))?no_esc} - + <@link.kw color="secondary" href=url.loginUrl size="small"> + ${kcSanitize(msg("backToLogin"))?no_esc} + diff --git a/theme/keywind/login/login-update-password.ftl b/theme/keywind/login/login-update-password.ftl index 84d3037..ed82380 100644 --- a/theme/keywind/login/login-update-password.ftl +++ b/theme/keywind/login/login-update-password.ftl @@ -1,6 +1,9 @@ <#import "template.ftl" as layout> -<#import "components/button/primary.ftl" as buttonPrimary> -<#import "components/input/primary.ftl" as inputPrimary> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> +<#import "components/atoms/checkbox.ftl" as checkbox> +<#import "components/atoms/form.ftl" as form> +<#import "components/atoms/input.ftl" as input> <@layout.registrationLayout displayMessage=!messagesPerField.existsError("password", "password-confirm") @@ -10,7 +13,7 @@ <#if section="header"> ${msg("updatePasswordTitle")} <#elseif section="form"> -
    + <@form.kw action=url.loginAction method="post"> -
    - <@inputPrimary.kw - autocomplete="new-password" - autofocus=true - invalid=["password", "password-confirm"] - message=false - name="password-new" - type="password" - > - ${msg("passwordNew")} - -
    -
    - <@inputPrimary.kw - autocomplete="new-password" - invalid=["password-confirm"] - name="password-confirm" - type="password" - > - ${msg("passwordConfirm")} - -
    - - <#-- TODO isAppInitiatedAction --> - -
    - <@buttonPrimary.kw type="submit"> - ${msg("doSubmit")} - -
    -
    + <@input.kw + autocomplete="new-password" + autofocus=true + invalid=messagesPerField.existsError("password", "password-confirm") + label=msg("passwordNew") + name="password-new" + type="password" + /> + <@input.kw + autocomplete="new-password" + invalid=messagesPerField.existsError("password-confirm") + label=msg("passwordConfirm") + message=kcSanitize(messagesPerField.get("password-confirm")) + name="password-confirm" + type="password" + /> + <#if isAppInitiatedAction??> + <@checkbox.kw + checked=true + label=msg("logoutOtherSessions") + name="logout-sessions" + value="on" + /> + + <@buttonGroup.kw> + <#if isAppInitiatedAction??> + <@button.kw color="primary" type="submit"> + ${msg("doSubmit")} + + <@button.kw color="secondary" name="cancel-aia" type="submit" value="true"> + ${msg("doCancel")} + + <#else> + <@button.kw color="primary" type="submit"> + ${msg("doSubmit")} + + + + diff --git a/theme/keywind/login/login-update-profile.ftl b/theme/keywind/login/login-update-profile.ftl index 6c538e9..306bad9 100644 --- a/theme/keywind/login/login-update-profile.ftl +++ b/theme/keywind/login/login-update-profile.ftl @@ -1,6 +1,8 @@ <#import "template.ftl" as layout> -<#import "components/button/primary.ftl" as buttonPrimary> -<#import "components/input/primary.ftl" as inputPrimary> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> +<#import "components/atoms/form.ftl" as form> +<#import "components/atoms/input.ftl" as input> <@layout.registrationLayout displayMessage=!messagesPerField.existsError("email", "firstName", "lastName", "username") @@ -10,62 +12,60 @@ <#if section="header"> ${msg("loginProfileTitle")} <#elseif section="form"> -
    + <@form.kw action=url.loginAction method="post"> <#if user.editUsernameAllowed> -
    - <@inputPrimary.kw - autocomplete="username" - autofocus=true - invalid=["username"] - name="username" - type="text" - value=(user.username)!'' - > - ${msg("username")} - -
    + <@input.kw + autocomplete="username" + autofocus=true + invalid=messagesPerField.existsError("username") + label=msg("username") + message=kcSanitize(messagesPerField.get("username")) + name="username" + type="text" + value=(user.username)!'' + /> -
    - <@inputPrimary.kw - autocomplete="email" - invalid=["email"] - name="email" - type="email" - value=(user.email)!'' - > - ${msg("email")} - -
    -
    - <@inputPrimary.kw - autocomplete="given-name" - invalid=["firstName"] - name="firstName" - type="text" - value=(user.firstName)!'' - > - ${msg("firstName")} - -
    -
    - <@inputPrimary.kw - autocomplete="family-name" - invalid=["lastName"] - name="lastName" - type="text" - value=(user.lastName)!'' - > - ${msg("lastName")} - -
    - - <#-- TODO isAppInitiatedAction --> - -
    - <@buttonPrimary.kw type="submit"> - ${msg("doSubmit")} - -
    -
    + <@input.kw + autocomplete="email" + invalid=messagesPerField.existsError("email") + label=msg("email") + message=kcSanitize(messagesPerField.get("email")) + name="email" + type="email" + value=(user.email)!'' + /> + <@input.kw + autocomplete="given-name" + invalid=messagesPerField.existsError("firstName") + label=msg("firstName") + message=kcSanitize(messagesPerField.get("firstName")) + name="firstName" + type="text" + value=(user.firstName)!'' + /> + <@input.kw + autocomplete="family-name" + invalid=messagesPerField.existsError("lastName") + label=msg("lastName") + message=kcSanitize(messagesPerField.get("lastName")) + name="lastName" + type="text" + value=(user.lastName)!'' + /> + <@buttonGroup.kw> + <#if isAppInitiatedAction??> + <@button.kw color="primary" type="submit"> + ${msg("doSubmit")} + + <@button.kw color="secondary" name="cancel-aia" type="submit" value="true"> + ${msg("doCancel")} + + <#else> + <@button.kw color="primary" type="submit"> + ${msg("doSubmit")} + + + + diff --git a/theme/keywind/login/login.ftl b/theme/keywind/login/login.ftl index 1ff0664..60a5d8b 100644 --- a/theme/keywind/login/login.ftl +++ b/theme/keywind/login/login.ftl @@ -1,10 +1,14 @@ <#import "template.ftl" as layout> -<#import "components/provider.ftl" as provider> -<#import "components/button/primary.ftl" as buttonPrimary> -<#import "components/checkbox/primary.ftl" as checkboxPrimary> -<#import "components/input/primary.ftl" as inputPrimary> -<#import "components/label/username.ftl" as labelUsername> -<#import "components/link/primary.ftl" as linkPrimary> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> +<#import "components/atoms/checkbox.ftl" as checkbox> +<#import "components/atoms/form.ftl" as form> +<#import "components/atoms/input.ftl" as input> +<#import "components/atoms/link.ftl" as link> +<#import "components/molecules/identity-provider.ftl" as identityProvider> +<#import "features/labels/username.ftl" as usernameLabel> + +<#assign usernameLabel><@usernameLabel.kw /> <@layout.registrationLayout displayInfo=realm.password && realm.registrationAllowed && !registrationDisabled?? @@ -16,9 +20,8 @@ ${msg("loginAccountTitle")} <#elseif section="form"> <#if realm.password> -
    @@ -27,58 +30,56 @@ type="hidden" value="<#if auth.selectedCredential?has_content>${auth.selectedCredential}" > -
    - <@inputPrimary.kw - autocomplete=realm.loginWithEmailAllowed?string("email", "username") - autofocus=true - disabled=usernameEditDisabled?? - invalid=["username", "password"] - name="username" - type="text" - value=(login.username)!'' - > - <@labelUsername.kw /> - -
    -
    - <@inputPrimary.kw - invalid=["username", "password"] - message=false - name="password" - type="password" - > - ${msg("password")} - -
    -
    - <#if realm.rememberMe && !usernameEditDisabled??> - <@checkboxPrimary.kw checked=login.rememberMe?? name="rememberMe"> - ${msg("rememberMe")} - - - <#if realm.resetPasswordAllowed> - <@linkPrimary.kw href=url.loginResetCredentialsUrl> - ${msg("doForgotPassword")} - - -
    -
    - <@buttonPrimary.kw name="login" type="submit"> + <@input.kw + autocomplete=realm.loginWithEmailAllowed?string("email", "username") + autofocus=true + disabled=usernameEditDisabled?? + invalid=messagesPerField.existsError("username", "password") + label=usernameLabel + message=kcSanitize(messagesPerField.getFirstError("username", "password")) + name="username" + type="text" + value=(login.username)!'' + /> + <@input.kw + invalid=messagesPerField.existsError("username", "password") + label=msg("password") + name="password" + type="password" + /> + <#if realm.rememberMe && !usernameEditDisabled?? || realm.resetPasswordAllowed> +
    + <#if realm.rememberMe && !usernameEditDisabled??> + <@checkbox.kw + checked=login.rememberMe?? + label=msg("rememberMe") + name="rememberMe" + /> + + <#if realm.resetPasswordAllowed> + <@link.kw color="primary" href=url.loginResetCredentialsUrl size="small"> + ${msg("doForgotPassword")} + + +
    + + <@buttonGroup.kw> + <@button.kw color="primary" name="login" type="submit"> ${msg("doLogIn")} - -
    -
    + + + <#if realm.password && social.providers??> - <@provider.kw /> + <@identityProvider.kw providers=social.providers /> <#elseif section="info"> <#if realm.password && realm.registrationAllowed && !registrationDisabled??>
    ${msg("noAccount")} - <@linkPrimary.kw href=url.registrationUrl> + <@link.kw color="primary" href=url.registrationUrl> ${msg("doRegister")} - +
    diff --git a/theme/keywind/login/logout-confirm.ftl b/theme/keywind/login/logout-confirm.ftl index 3f2bc36..e7ec486 100644 --- a/theme/keywind/login/logout-confirm.ftl +++ b/theme/keywind/login/logout-confirm.ftl @@ -1,23 +1,24 @@ <#import "template.ftl" as layout> -<#import "components/button/primary.ftl" as buttonPrimary> -<#import "components/link/secondary.ftl" as linkSecondary> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/form.ftl" as form> +<#import "components/atoms/link.ftl" as link> <@layout.registrationLayout; section> - <#if section = "header"> + <#if section="header"> ${msg("logoutConfirmTitle")} - <#elseif section = "form"> + <#elseif section="form">

    ${msg("logoutConfirmHeader")}

    -
    + <@form.kw action=url.logoutConfirmAction method="post"> - <@buttonPrimary.kw name="confirmLogout" type="submit" value="${msg('doLogout')}"> + <@button.kw color="primary" name="confirmLogout" type="submit" value=msg('doLogout')> ${msg("doLogout")} - -
    + + <#if !logoutConfirm.skipLink> <#if (client.baseUrl)?has_content> - <@linkSecondary.kw href=client.baseUrl> - ${kcSanitize(msg("backToApplication"))?no_esc} - + <@link.kw color="secondary" href=client.baseUrl size="small"> + ${kcSanitize(msg("backToApplication"))?no_esc} + diff --git a/theme/keywind/login/register.ftl b/theme/keywind/login/register.ftl index c123440..c1a2f06 100644 --- a/theme/keywind/login/register.ftl +++ b/theme/keywind/login/register.ftl @@ -1,7 +1,9 @@ <#import "template.ftl" as layout> -<#import "components/button/primary.ftl" as buttonPrimary> -<#import "components/input/primary.ftl" as inputPrimary> -<#import "components/link/secondary.ftl" as linkSecondary> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> +<#import "components/atoms/form.ftl" as form> +<#import "components/atoms/input.ftl" as input> +<#import "components/atoms/link.ftl" as link> <@layout.registrationLayout displayMessage=!messagesPerField.existsError("firstName", "lastName", "email", "username", "password", "password-confirm") @@ -11,90 +13,76 @@ <#if section="header"> ${msg("registerTitle")} <#elseif section="form"> -
    -
    - <@inputPrimary.kw - autocomplete="given-name" - autofocus=true - invalid=["firstName"] - name="firstName" - type="text" - value=(register.formData.firstName)!'' - > - ${msg("firstName")} - -
    -
    - <@inputPrimary.kw - autocomplete="family-name" - invalid=["lastName"] - name="lastName" - type="text" - value=(register.formData.lastName)!'' - > - ${msg("lastName")} - -
    -
    - <@inputPrimary.kw - autocomplete="email" - invalid=["email"] - name="email" - type="email" - value=(register.formData.email)!'' - > - ${msg("email")} - -
    + <@form.kw action=url.registrationAction method="post"> + <@input.kw + autocomplete="given-name" + autofocus=true + invalid=messagesPerField.existsError("firstName") + label=msg("firstName") + message=kcSanitize(messagesPerField.get("firstName")) + name="firstName" + type="text" + value=(register.formData.firstName)!'' + /> + <@input.kw + autocomplete="family-name" + invalid=messagesPerField.existsError("lastName") + label=msg("lastName") + message=kcSanitize(messagesPerField.get("lastName")) + name="lastName" + type="text" + value=(register.formData.lastName)!'' + /> + <@input.kw + autocomplete="email" + invalid=messagesPerField.existsError("email") + label=msg("email") + message=kcSanitize(messagesPerField.get("email")) + name="email" + type="email" + value=(register.formData.email)!'' + /> <#if !realm.registrationEmailAsUsername> -
    - <@inputPrimary.kw - autocomplete="username" - invalid=["username"] - name="username" - type="text" - value=(register.formData.username)!'' - > - ${msg("username")} - -
    + <@input.kw + autocomplete="username" + invalid=messagesPerField.existsError("username") + label=msg("username") + message=kcSanitize(messagesPerField.get("username")) + name="username" + type="text" + value=(register.formData.username)!'' + /> <#if passwordRequired??> -
    - <@inputPrimary.kw - autocomplete="new-password" - invalid=["password", "password-confirm"] - name="password" - type="password" - > - ${msg("password")} - -
    -
    - <@inputPrimary.kw - autocomplete="new-password" - invalid=["password-confirm"] - name="password-confirm" - type="password" - > - ${msg("passwordConfirm")} - -
    + <@input.kw + autocomplete="new-password" + invalid=messagesPerField.existsError("password", "password-confirm") + label=msg("password") + message=kcSanitize(messagesPerField.getFirstError("password", "password-confirm")) + name="password" + type="password" + /> + <@input.kw + autocomplete="new-password" + invalid=messagesPerField.existsError("password-confirm") + label=msg("passwordConfirm") + message=kcSanitize(messagesPerField.get("password-confirm")) + name="password-confirm" + type="password" + /> <#if recaptchaRequired??> -
    -
    -
    +
    -
    - <@buttonPrimary.kw type="submit"> + <@buttonGroup.kw> + <@button.kw color="primary" type="submit"> ${msg("doRegister")} - -
    -
    + + + <#elseif section="nav"> - <@linkSecondary.kw href=url.loginUrl> - ${kcSanitize(msg("backToLogin"))?no_esc} - + <@link.kw color="secondary" href=url.loginUrl size="small"> + ${kcSanitize(msg("backToLogin"))?no_esc} + diff --git a/theme/keywind/login/resources/dist/index.css b/theme/keywind/login/resources/dist/index.css index 5d764ab..baad0bf 100644 --- a/theme/keywind/login/resources/dist/index.css +++ b/theme/keywind/login/resources/dist/index.css @@ -1 +1 @@ -*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}[type=text],[type=email],[type=url],[type=password],[type=number],[type=date],[type=datetime-local],[type=month],[type=search],[type=tel],[type=time],[type=week],[multiple],textarea,select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow: 0 0 #0000}[type=text]:focus,[type=email]:focus,[type=url]:focus,[type=password]:focus,[type=number]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=month]:focus,[type=search]:focus,[type=tel]:focus,[type=time]:focus,[type=week]:focus,[multiple]:focus,textarea:focus,select:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em}::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field{padding-top:0;padding-bottom:0}select{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple]{background-image:initial;background-position:initial;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#2563eb;background-color:#fff;border-color:#6b7280;border-width:1px;--tw-shadow: 0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 2px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}[type=checkbox]:checked,[type=radio]:checked{border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}[type=checkbox]:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e")}[type=radio]:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e")}[type=checkbox]:checked:hover,[type=checkbox]:checked:focus,[type=radio]:checked:hover,[type=radio]:checked:focus{border-color:transparent;background-color:currentColor}[type=checkbox]:indeterminate{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}[type=checkbox]:indeterminate:hover,[type=checkbox]:indeterminate:focus{border-color:transparent;background-color:currentColor}[type=file]{background:unset;border-color:inherit;border-width:0;border-radius:0;padding:0;font-size:unset;line-height:inherit}[type=file]:focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}.shadow-lg{--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000}.focus\:ring-2,.focus\:ring{--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000}.container{width:100%}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}[x-cloak]{display:none!important}.separate{display:flex;align-items:center;text-align:center}.separate:after,.separate:before{content:"";flex:1 1 0%;border-bottom-width:1px;--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity))}.separate:not(:empty):after{margin-left:.5rem}.separate:not(:empty):before{margin-right:.5rem}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.absolute{position:absolute}.relative{position:relative}.bottom-0{bottom:0px}.-left-4{left:-1rem}.m-0{margin:0}.mx-auto{margin-left:auto;margin-right:auto}.mb-4{margin-bottom:1rem}.ml-2{margin-left:.5rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mr-1{margin-right:.25rem}.mb-6{margin-bottom:1.5rem}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.h-6{height:1.5rem}.h-4{height:1rem}.max-h-80{max-height:20rem}.min-h-screen{min-height:100vh}.w-6{width:1.5rem}.w-full{width:100%}.w-4{width:1rem}.max-w-md{max-width:28rem}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-around{justify-content:space-around}.gap-4{gap:1rem}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(1rem * var(--tw-space-x-reverse));margin-left:calc(1rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.overflow-y-scroll{overflow-y:scroll}.rounded-lg{border-radius:.5rem}.rounded{border-radius:.25rem}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-secondary-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity))}.bg-primary-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity))}.bg-secondary-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.bg-red-100{--tw-bg-opacity: 1;background-color:rgb(254 226 226 / var(--tw-bg-opacity))}.bg-blue-100{--tw-bg-opacity: 1;background-color:rgb(219 234 254 / var(--tw-bg-opacity))}.bg-green-100{--tw-bg-opacity: 1;background-color:rgb(220 252 231 / var(--tw-bg-opacity))}.bg-orange-100{--tw-bg-opacity: 1;background-color:rgb(255 237 213 / var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.p-4{padding:1rem}.p-8{padding:2rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.px-4{padding-left:1rem;padding-right:1rem}.pl-4{padding-left:1rem}.pt-4{padding-top:1rem}.text-center{text-align:center}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-2xl{font-size:1.5rem;line-height:2rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.text-secondary-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.text-primary-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity))}.text-secondary-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity))}.text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity))}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity))}.text-green-600{--tw-text-opacity: 1;color:rgb(22 163 74 / var(--tw-text-opacity))}.text-orange-600{--tw-text-opacity: 1;color:rgb(234 88 12 / var(--tw-text-opacity))}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.hover\:border-transparent:hover{border-color:transparent}.hover\:bg-provider-bitbucket\/10:hover{background-color:#0052cc1a}.hover\:bg-provider-facebook\/10:hover{background-color:#1877f21a}.hover\:bg-provider-github\/10:hover{background-color:#1817171a}.hover\:bg-provider-gitlab\/10:hover{background-color:#fc6d261a}.hover\:bg-provider-google\/10:hover{background-color:#4285f41a}.hover\:bg-provider-instagram\/10:hover{background-color:#e4405f1a}.hover\:bg-provider-linkedin\/10:hover{background-color:#0a66c21a}.hover\:bg-provider-microsoft\/10:hover{background-color:#5e5e5e1a}.hover\:bg-provider-oidc\/10:hover{background-color:#f78c401a}.hover\:bg-provider-openshift\/10:hover{background-color:#ee00001a}.hover\:bg-provider-paypal\/10:hover{background-color:#00457c1a}.hover\:bg-provider-stackoverflow\/10:hover{background-color:#f580251a}.hover\:bg-provider-twitter\/10:hover{background-color:#1da1f21a}.hover\:bg-secondary-100:hover{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.hover\:bg-primary-700:hover{--tw-bg-opacity: 1;background-color:rgb(29 78 216 / var(--tw-bg-opacity))}.hover\:bg-secondary-200:hover{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity))}.hover\:text-secondary-900:hover{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity))}.hover\:text-primary-500:hover{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity))}.focus\:border-primary-300:focus{--tw-border-opacity: 1;border-color:rgb(147 197 253 / var(--tw-border-opacity))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-primary-600:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(37 99 235 / var(--tw-ring-opacity))}.focus\:ring-secondary-600:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(75 85 99 / var(--tw-ring-opacity))}.focus\:ring-primary-200:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(191 219 254 / var(--tw-ring-opacity))}.focus\:ring-opacity-50:focus{--tw-ring-opacity: .5}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}@media (min-width: 640px){.sm\:py-16{padding-top:4rem;padding-bottom:4rem}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}} +*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}[type=text],[type=email],[type=url],[type=password],[type=number],[type=date],[type=datetime-local],[type=month],[type=search],[type=tel],[type=time],[type=week],[multiple],textarea,select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow: 0 0 #0000}[type=text]:focus,[type=email]:focus,[type=url]:focus,[type=password]:focus,[type=number]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=month]:focus,[type=search]:focus,[type=tel]:focus,[type=time]:focus,[type=week]:focus,[multiple]:focus,textarea:focus,select:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em}::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field{padding-top:0;padding-bottom:0}select{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple]{background-image:initial;background-position:initial;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#2563eb;background-color:#fff;border-color:#6b7280;border-width:1px;--tw-shadow: 0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 2px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}[type=checkbox]:checked,[type=radio]:checked{border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}[type=checkbox]:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e")}[type=radio]:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e")}[type=checkbox]:checked:hover,[type=checkbox]:checked:focus,[type=radio]:checked:hover,[type=radio]:checked:focus{border-color:transparent;background-color:currentColor}[type=checkbox]:indeterminate{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}[type=checkbox]:indeterminate:hover,[type=checkbox]:indeterminate:focus{border-color:transparent;background-color:currentColor}[type=file]{background:unset;border-color:inherit;border-width:0;border-radius:0;padding:0;font-size:unset;line-height:inherit}[type=file]:focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}.shadow-lg{--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000}.focus\:ring-2,.focus\:ring{--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000}.container{width:100%}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}[x-cloak]{display:none!important}.separate{display:flex;align-items:center;text-align:center}.separate:after,.separate:before{content:"";flex:1 1 0%;border-bottom-width:1px;--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity))}.separate:not(:empty):after{margin-left:.5rem}.separate:not(:empty):before{margin-right:.5rem}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.absolute{position:absolute}.relative{position:relative}.bottom-0{bottom:0px}.-left-4{left:-1rem}.m-0{margin:0}.mx-auto{margin-left:auto;margin-right:auto}.mb-4{margin-bottom:1rem}.ml-2{margin-left:.5rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mr-1{margin-right:.25rem}.mb-6{margin-bottom:1.5rem}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.h-5{height:1.25rem}.h-4{height:1rem}.h-6{height:1.5rem}.max-h-80{max-height:20rem}.min-h-screen{min-height:100vh}.w-5{width:1.25rem}.w-full{width:100%}.w-4{width:1rem}.w-6{width:1.5rem}.max-w-md{max-width:28rem}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-around{justify-content:space-around}.gap-4{gap:1rem}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(1rem * var(--tw-space-x-reverse));margin-left:calc(1rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.overflow-y-scroll{overflow-y:scroll}.rounded-lg{border-radius:.5rem}.rounded{border-radius:.25rem}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-secondary-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity))}.bg-red-100{--tw-bg-opacity: 1;background-color:rgb(254 226 226 / var(--tw-bg-opacity))}.bg-blue-100{--tw-bg-opacity: 1;background-color:rgb(219 234 254 / var(--tw-bg-opacity))}.bg-green-100{--tw-bg-opacity: 1;background-color:rgb(220 252 231 / var(--tw-bg-opacity))}.bg-orange-100{--tw-bg-opacity: 1;background-color:rgb(255 237 213 / var(--tw-bg-opacity))}.bg-secondary-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.bg-primary-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.p-4{padding:1rem}.p-8{padding:2rem}.px-4{padding-left:1rem;padding-right:1rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.pl-4{padding-left:1rem}.pt-4{padding-top:1rem}.text-center{text-align:center}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.text-2xl{font-size:1.5rem;line-height:2rem}.font-bold{font-weight:700}.text-secondary-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity))}.text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity))}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity))}.text-green-600{--tw-text-opacity: 1;color:rgb(22 163 74 / var(--tw-text-opacity))}.text-orange-600{--tw-text-opacity: 1;color:rgb(234 88 12 / var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.text-primary-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity))}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.hover\:border-transparent:hover{border-color:transparent}.hover\:bg-primary-700:hover{--tw-bg-opacity: 1;background-color:rgb(29 78 216 / var(--tw-bg-opacity))}.hover\:bg-secondary-200:hover{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity))}.hover\:bg-provider-bitbucket\/10:hover{background-color:#0052cc1a}.hover\:bg-provider-facebook\/10:hover{background-color:#1877f21a}.hover\:bg-provider-github\/10:hover{background-color:#1817171a}.hover\:bg-provider-gitlab\/10:hover{background-color:#fc6d261a}.hover\:bg-provider-google\/10:hover{background-color:#4285f41a}.hover\:bg-provider-instagram\/10:hover{background-color:#e4405f1a}.hover\:bg-provider-linkedin\/10:hover{background-color:#0a66c21a}.hover\:bg-provider-microsoft\/10:hover{background-color:#5e5e5e1a}.hover\:bg-provider-oidc\/10:hover{background-color:#f78c401a}.hover\:bg-provider-openshift\/10:hover{background-color:#ee00001a}.hover\:bg-provider-paypal\/10:hover{background-color:#00457c1a}.hover\:bg-provider-stackoverflow\/10:hover{background-color:#f580251a}.hover\:bg-provider-twitter\/10:hover{background-color:#1da1f21a}.hover\:bg-secondary-100:hover{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.hover\:text-secondary-900:hover{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity))}.hover\:text-primary-500:hover{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity))}.focus\:border-primary-300:focus{--tw-border-opacity: 1;border-color:rgb(147 197 253 / var(--tw-border-opacity))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-primary-600:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(37 99 235 / var(--tw-ring-opacity))}.focus\:ring-secondary-600:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(75 85 99 / var(--tw-ring-opacity))}.focus\:ring-primary-200:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(191 219 254 / var(--tw-ring-opacity))}.focus\:ring-opacity-50:focus{--tw-ring-opacity: .5}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}@media (min-width: 640px){.sm\:py-16{padding-top:4rem;padding-bottom:4rem}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}} diff --git a/theme/keywind/login/template.ftl b/theme/keywind/login/template.ftl index 744f520..e7cd58b 100644 --- a/theme/keywind/login/template.ftl +++ b/theme/keywind/login/template.ftl @@ -1,67 +1,83 @@ -<#import "components/document.ftl" as document> -<#import "components/layout/alerts.ftl" as alerts> -<#import "components/layout/another-way.ftl" as anotherWay> -<#import "components/layout/card.ftl" as card> -<#import "components/layout/card-footer.ftl" as cardFooter> -<#import "components/layout/card-header.ftl" as cardHeader> -<#import "components/layout/card-main.ftl" as cardMain> -<#import "components/layout/container.ftl" as container> -<#import "components/layout/locales.ftl" as locales> -<#import "components/layout/nav.ftl" as nav> -<#import "components/layout/required-fields.ftl" as requiredFields> -<#import "components/layout/title.ftl" as title> -<#import "components/layout/subtitle.ftl" as subtitle> -<#import "components/layout/username.ftl" as username> +<#import "document.ftl" as document> +<#import "components/atoms/alert.ftl" as alert> +<#import "components/atoms/body.ftl" as body> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/card.ftl" as card> +<#import "components/atoms/container.ftl" as container> +<#import "components/atoms/heading.ftl" as heading> +<#import "components/atoms/logo.ftl" as logo> +<#import "components/atoms/nav.ftl" as nav> +<#import "components/molecules/locale-provider.ftl" as localeProvider> +<#import "components/molecules/username.ftl" as username> <#macro registrationLayout displayInfo=false displayMessage=true displayRequiredFields=false + script="" showAnotherWayIfPresent=true > + <#assign cardHeader> + <@logo.kw> + ${kcSanitize(msg("loginTitleHtml", (realm.displayNameHtml!"")))?no_esc} + + <#if !(auth?has_content && auth.showUsername() && !auth.showResetCredentials())> + <@heading.kw> + <#nested "header"> + + <#else> + <#nested "show-username"> + <@username.kw + linkHref=url.loginRestartFlowUrl + linkTitle=msg("restartLoginTooltip") + name=auth.attemptedUsername + /> + + + + <#assign cardContent> + <#if displayMessage && message?has_content && (message.type != "warning" || !isAppInitiatedAction??)> + <@alert.kw color=message.type> + ${kcSanitize(message.summary)?no_esc} + + + <#nested "form"> + <#if displayRequiredFields> +

    + * ${msg("requiredFields")} +

    + + <#if auth?has_content && auth.showTryAnotherWayLink() && showAnotherWayIfPresent> +
    + + <@button.kw color="primary" type="submit"> + ${msg("doTryAnotherWay")} + +
    + + + + <#assign cardFooter> + <#if displayInfo> + <#nested "info"> + + + - <@document.kw /> + <@document.kw script=script /> - + <@body.kw> <@container.kw> - <@card.kw> - <@cardHeader.kw> - <@title.kw /> - <#if !(auth?has_content && auth.showUsername() && !auth.showResetCredentials())> - <@subtitle.kw> - <#nested "header"> - - <#else> - <@username.kw /> - - - <@cardMain.kw> - <#if displayMessage && message?has_content && (message.type != "warning" || !isAppInitiatedAction??)> - <@alerts.kw /> - - <#nested "form"> - <#if displayRequiredFields> - <@requiredFields.kw /> - - <#if auth?has_content && auth.showTryAnotherWayLink() && showAnotherWayIfPresent> - <@anotherWay.kw /> - - - <#if displayInfo> - <@cardFooter.kw> - <#nested "info"> - - - + <@card.kw content=cardContent footer=cardFooter header=cardHeader /> <@nav.kw> <#nested "nav"> <#if realm.internationalizationEnabled && locale.supported?size gt 1> - <@locales.kw /> + <@localeProvider.kw currentLocale=locale.current locales=locale.supported /> - +