Skip to content
Merged
6 changes: 6 additions & 0 deletions packages/boxel-ui/addon/raw-icons/google-color.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions packages/boxel-ui/addon/src/icons.gts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import Fitted from './icons/fitted.gts';
import Folder from './icons/folder.gts';
import Form from './icons/form.gts';
import FourLines from './icons/four-lines.gts';
import GoogleColor from './icons/google-color.gts';
import Grid3x3 from './icons/grid-3x3.gts';
import Group from './icons/group.gts';
import Head from './icons/head.gts';
Expand Down Expand Up @@ -116,6 +117,7 @@ export const ALL_ICON_COMPONENTS = [
Folder,
Form,
FourLines,
GoogleColor,
Grid3x3,
Group,
Head,
Expand Down Expand Up @@ -198,6 +200,7 @@ export {
Folder,
Form,
FourLines,
GoogleColor,
Grid3x3,
Group,
Head,
Expand Down
30 changes: 30 additions & 0 deletions packages/boxel-ui/addon/src/icons/google-color.gts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// This file is auto-generated by 'pnpm rebuild:icons'
import type { TemplateOnlyComponent } from '@ember/component/template-only';

import type { Signature } from './types.ts';

const IconComponent: TemplateOnlyComponent<Signature> = <template>
<svg
xmlns='http://www.w3.org/2000/svg'
width='18'
height='18'
viewBox='0 0 18 18'
...attributes
><path
fill='#4285F4'
d='M17.64 9.205q-.002-.958-.164-1.841H9v3.481h4.844a4.14 4.14 0 0 1-1.795 2.717v2.258h2.909c1.702-1.567 2.682-3.874 2.682-6.615'
/><path
fill='#34A853'
d='M9 18c2.43 0 4.467-.806 5.956-2.18l-2.908-2.259c-.806.54-1.837.86-3.048.86-2.344 0-4.328-1.584-5.036-3.711H.957v2.332A9 9 0 0 0 9 18'
/><path
fill='#FBBC05'
d='M3.964 10.71c-.18-.54-.282-1.117-.282-1.71s.102-1.17.282-1.71V4.958H.957C.347 6.173 0 7.548 0 9s.348 2.827.957 4.042z'
/><path
fill='#EA4335'
d='M9 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.581C13.463.892 11.426 0 9 0A9 9 0 0 0 .957 4.958L3.964 7.29C4.672 5.163 6.656 3.58 9 3.58'
/></svg>
</template>;

// @ts-expect-error this is the only way to set a name on a Template Only Component currently
IconComponent.name = 'GoogleColor';
export default IconComponent;
84 changes: 84 additions & 0 deletions packages/host/app/components/matrix/auth-button.gts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import type { TemplateOnlyComponent } from '@ember/component/template-only';

import { Button } from '@cardstack/boxel-ui/components';
import { eq } from '@cardstack/boxel-ui/helpers';

export type AuthButtonVariant = 'primary' | 'secondary' | 'google';

interface Signature {
Element: HTMLButtonElement;
Args: {
variant?: AuthButtonVariant;
disabled?: boolean;
loading?: boolean;
};
Blocks: { default: [] };
}

const AuthButton: TemplateOnlyComponent<Signature> = <template>
{{#if (eq @variant 'primary')}}
<Button
class='auth-btn auth-btn--primary'
@kind='primary'
@disabled={{@disabled}}
@loading={{@loading}}
...attributes
>{{yield}}</Button>
{{else if (eq @variant 'google')}}
<Button
class='auth-btn auth-btn--google'
@kind='secondary-dark'
@disabled={{@disabled}}
@loading={{@loading}}
...attributes
>{{yield}}</Button>
{{else}}
<Button
class='auth-btn auth-btn--secondary'
@kind='secondary-dark'
@disabled={{@disabled}}
@loading={{@loading}}
...attributes
>{{yield}}</Button>
{{/if}}

<style scoped>
.auth-btn {
--boxel-button-padding: var(--boxel-sp-sm);
width: 100%;
}
.auth-btn :deep(.boxel-loading-indicator) {
display: flex;
justify-content: center;
align-items: center;
}
.auth-btn--primary {
--boxel-button-color: var(--auth-primary-bg);
--boxel-button-text-color: var(--auth-primary-text);
}
.auth-btn--primary:disabled {
--boxel-button-color: var(--auth-primary-disabled-bg);
--boxel-button-text-color: var(--auth-primary-disabled-text);
--boxel-button-border: none;
}
.auth-btn--secondary,
.auth-btn--google {
--boxel-button-color: var(--auth-secondary-bg);
--boxel-button-text-color: var(--auth-secondary-text);
--boxel-button-border: 1px solid var(--auth-secondary-border);
}
.auth-btn--secondary:not(:disabled):hover,
.auth-btn--secondary:not(:disabled):active,
.auth-btn--google:not(:disabled):hover,
.auth-btn--google:not(:disabled):active {
--boxel-button-color: var(--auth-secondary-hover-bg);
}
.auth-btn--google {
display: inline-flex;
align-items: center;
gap: var(--boxel-sp-xs);
}
</style>
</template>;

export default AuthButton;
84 changes: 53 additions & 31 deletions packages/host/app/components/matrix/auth-container.gts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { TemplateOnlyComponent } from '@ember/component/template-only';

import { CardContainer, BoxelHeader } from '@cardstack/boxel-ui/components';
import { BoxelIcon } from '@cardstack/boxel-ui/icons';

interface Signature {
Expand All @@ -11,54 +10,77 @@ interface Signature {

const AuthContainer: TemplateOnlyComponent<Signature> = <template>
<div class='auth'>
<div class='logo' aria-hidden='true'>
<BoxelIcon />
</div>
<div class='container'>
<CardContainer class='auth-container'>
<BoxelHeader @title='Boxel' class='header'>
<:icon>
<BoxelIcon />
</:icon>
</BoxelHeader>
<div class='content'>
{{yield}}
</div>
</CardContainer>
<div class='content'>
{{yield}}
</div>
</div>
</div>

<style scoped>
.auth {
height: 100%;
position: relative;
min-height: 100dvh;
overflow: auto;
}
background-color: var(--background);

/*
* shadcn-style aliases — BoxelInput and the secondary-dark Button kind
* read these, so the dark theme cascades to every child of the auth
* dispatcher (login / register / forgot-password) without per-component
* styling. Only the values without a boxel token are spelled as raw hex.
*/
--background: #191624;
--foreground: var(--boxel-light);
--border: #525252;
--muted-foreground: #7f7c8c;
--ring: var(--boxel-highlight);

color: var(--foreground);

/*
* Auth-page shared button palette. Component-level overrides
* (login / register / forgot-password) read these so we don't repeat
* the same values per file.
*/
--auth-primary-bg: var(--boxel-highlight);
--auth-primary-text: var(--boxel-dark);
--auth-primary-disabled-bg: #444051;
--auth-primary-disabled-text: #817c93;
--auth-secondary-bg: var(--background);
--auth-secondary-border: var(--border);
--auth-secondary-text: var(--foreground);
--auth-secondary-hover-bg: #221f30;
}
.logo {
position: absolute;
top: var(--boxel-sp-lg);
left: var(--boxel-sp-lg);
--icon-color: var(--boxel-highlight);
width: 2rem;
height: 2rem;
}
.logo :deep(svg) {
width: 100%;
height: 100%;
}
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100%;
min-height: 100dvh;
padding: var(--boxel-sp-lg);
}

.auth-container {
background-color: var(--boxel-light);
border: 1px solid var(--boxel-form-control-border-color);
border-radius: var(--boxel-form-control-border-radius);
letter-spacing: var(--boxel-lsp);
max-width: 34.375rem;
position: relative;
}
.header {
text-transform: uppercase;
gap: var(--boxel-sp-xxs);
font: 700 var(--boxel-font);
letter-spacing: var(--boxel-lsp-lg);
}
.content {
display: flex;
flex-direction: column;
padding: var(--boxel-sp) var(--boxel-sp-xl) calc(var(--boxel-sp) * 2)
var(--boxel-sp-xl);
width: 100%;
max-width: 25rem;
padding: var(--boxel-sp) 0 calc(var(--boxel-sp) * 2) 0;
}
</style>
</template>;
Expand Down
60 changes: 60 additions & 0 deletions packages/host/app/components/matrix/auth-form-field.gts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import type { TemplateOnlyComponent } from '@ember/component/template-only';

import { FieldContainer } from '@cardstack/boxel-ui/components';

interface Signature {
Element: HTMLElement;
Args: {
label: string;
};
Blocks: { default: [] };
}

const AuthFormField: TemplateOnlyComponent<Signature> = <template>
<FieldContainer
@label={{@label}}
@tag='label'
@vertical={{true}}
class='auth-form-field'
...attributes
>
{{yield}}
</FieldContainer>

<style scoped>
.auth-form-field {
margin-top: var(--boxel-sp);
}
.auth-form-field :deep(input:autofill) {
transition:
background-color 0s 600000s,
color 0s 600000s;
}
.auth-form-field :deep(.validation-icon-container.invalid) {
display: none;
}
.auth-form-field :deep(.validation-icon-container.valid svg) {
height: var(--boxel-sp-xs);
}
.auth-form-field :deep(.boxel-input-group--invalid > :nth-last-child(2)) {
border-top-right-radius: var(--boxel-input-group-border-radius);
border-bottom-right-radius: var(--boxel-input-group-border-radius);
border-right-width: var(--boxel-input-group-interior-border-width);
}
.auth-form-field
:deep(
.boxel-input-group:not(.boxel-input-group--invalid) > :nth-last-child(2)
) {
padding-right: 0;
}
.auth-form-field :deep(.error-message) {
margin-left: 0;
font: 500 var(--boxel-font-xs);
}
.auth-form-field :deep(.text-accessory) {
color: var(--muted-foreground);
}
</style>
</template>;

export default AuthFormField;
Loading
Loading