update design

This commit is contained in:
2025-11-30 22:35:24 +00:00
parent 19f016a476
commit e92bdeaa2b
7 changed files with 522 additions and 186 deletions
+247 -57
View File
@@ -32,89 +32,279 @@ export class IdpCenterContainer extends DeesElement {
cssManager.defaultStyles,
css`
:host {
font-family: 'Geist Sans';
--background: hsl(240 10% 3.9%);
--foreground: hsl(0 0% 98%);
--muted: hsl(240 3.7% 15.9%);
--muted-foreground: hsl(240 5% 64.9%);
--border: hsl(240 3.7% 15.9%);
--card: hsl(240 6% 6%);
font-family: 'Geist Sans', -apple-system, BlinkMacSystemFont, sans-serif;
position: absolute;
width: 100%;
height: 100%;
background: var(--background);
}
.mainContainer {
.split-container {
position: absolute;
top: 0px;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: grid;
grid-template-columns: 45% 55%;
}
/* Left Panel - Branding */
.brand-panel {
background: linear-gradient(135deg, hsl(240 10% 8%) 0%, hsl(240 10% 4%) 50%, hsl(240 12% 6%) 100%);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
opacity: 0;
transition: all 0.2s;
transition-delay: 0.05s;
transform: translate3d(0px, 8px, 0px);
pointer-events: none;
}
.mainContainer.show {
opacity: 1;
pointer-events: all;
transform: translate3d(0px, 0px, 0px);
}
.loginblock {
max-width: 500px;
flex-grow: 1;
transform: translate3d(0px, 0px, 0px);
transition: all 0.2s;
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2);
background: ${cssManager.bdTheme('#ffffff', '#111111')};
border-top: 1px solid ${cssManager.bdTheme('#ffffff', '#222222')};
border-radius: 16px;
padding: 48px;
position: relative;
overflow: hidden;
}
h1 {
font-size: 24px;
font-family: 'Cal Sans';
text-align: center;
letter-spacing:0.0125em;
.brand-panel::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: radial-gradient(ellipse at 30% 20%, hsla(240 20% 20% / 0.3) 0%, transparent 50%),
radial-gradient(ellipse at 70% 80%, hsla(240 20% 15% / 0.2) 0%, transparent 50%);
pointer-events: none;
}
.contentSpacer {
padding: 0px 0px 16px 0px;
.brand-content {
position: relative;
z-index: 1;
max-width: 400px;
}
.legalinfo {
.logo {
font-family: 'Cal Sans', 'Geist Sans', sans-serif;
font-size: 42px;
font-weight: 600;
color: var(--foreground);
margin: 0 0 12px 0;
letter-spacing: -0.02em;
}
.tagline {
font-size: 18px;
color: var(--muted-foreground);
margin: 0 0 48px 0;
line-height: 1.5;
}
.features {
display: flex;
flex-direction: column;
gap: 28px;
}
.feature {
display: flex;
align-items: flex-start;
gap: 16px;
}
.feature-icon {
width: 40px;
height: 40px;
border-radius: 10px;
background: hsla(240 10% 20% / 0.5);
border: 1px solid hsla(240 10% 30% / 0.3);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.feature-icon dees-icon {
color: var(--muted-foreground);
font-size: 18px;
}
.feature-text h3 {
font-size: 15px;
font-weight: 600;
color: var(--foreground);
margin: 0 0 4px 0;
}
.feature-text p {
font-size: 14px;
color: var(--muted-foreground);
margin: 0;
line-height: 1.4;
}
.learn-more {
margin-top: 48px;
}
/* Right Panel - Form */
.form-panel {
background: var(--card);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 48px;
position: relative;
border-left: 1px solid var(--border);
}
.form-content {
width: 100%;
max-width: 400px;
transform: translateY(8px);
opacity: 0;
transition: all 0.3s ease;
}
.form-content.show {
transform: translateY(0);
opacity: 1;
}
.form-footer {
position: absolute;
bottom: 24px;
left: 0;
right: 0;
text-align: center;
margin: auto;
color: ${cssManager.bdTheme('#666', '#ccc')};
font-size: 12px;
line-height: 100%;
padding: 8px;
background: ${cssManager.bdTheme('#f5f5f5', '#111')};
border-top: 1px solid ${cssManager.bdTheme('#ccc', '#222222')};
color: ${cssManager.bdTheme('#666', '#888')};
color: var(--muted-foreground);
}
.legalinfo a {
color: ${cssManager.bdTheme('#666', '#ccc')};
.form-footer a {
color: var(--muted-foreground);
text-decoration: none;
transition: color 0.15s ease;
}
.form-footer a:hover {
color: var(--foreground);
}
.form-footer .separator {
margin: 0 8px;
opacity: 0.5;
}
.version {
margin-top: 8px;
font-size: 11px;
opacity: 0.6;
}
/* Mobile Responsive */
@media (max-width: 900px) {
.split-container {
grid-template-columns: 1fr;
grid-template-rows: auto 1fr;
}
.brand-panel {
padding: 32px 24px;
min-height: auto;
}
.brand-content {
max-width: 100%;
}
.logo {
font-size: 32px;
}
.tagline {
font-size: 16px;
margin-bottom: 24px;
}
.features {
display: none;
}
.form-panel {
border-left: none;
border-top: 1px solid var(--border);
padding: 32px 24px;
}
}
`,
];
render() {
return html`
<div class="mainContainer">
<div class="loginblock">
<h1>idp.global</h1>
<div class="contentSpacer">
<div class="split-container">
<!-- Left: Branding Panel -->
<div class="brand-panel">
<div class="brand-content">
<h1 class="logo">idp.global</h1>
<p class="tagline">Your permanent identity on the web</p>
<div class="features">
<div class="feature">
<div class="feature-icon">
<dees-icon .icon=${'lucide:code'}></dees-icon>
</div>
<div class="feature-text">
<h3>Open Source</h3>
<p>Fully transparent, community-driven, no vendor lock-in</p>
</div>
</div>
<div class="feature">
<div class="feature-icon">
<dees-icon .icon=${'lucide:heart'}></dees-icon>
</div>
<div class="feature-text">
<h3>Always Free</h3>
<p>Free for individuals and organizations. Paid support available for SLAs</p>
</div>
</div>
<div class="feature">
<div class="feature-icon">
<dees-icon .icon=${'lucide:fingerprint'}></dees-icon>
</div>
<div class="feature-text">
<h3>Permanent Identity</h3>
<p>One identity across all your applications</p>
</div>
</div>
</div>
<div class="learn-more">
<dees-button
type="secondary"
@click=${() => window.open('https://about.idp.global', '_blank')}
>Learn more</dees-button>
</div>
</div>
</div>
<!-- Right: Form Panel -->
<div class="form-panel">
<div class="form-content">
<slot></slot>
</div>
<div class="legalinfo">
<a href="https://legal.task.vc/" target="_blank">Legal Info</a>
| <a href="https://task.vc/" target="_blank">Company Website</a>
| <a href="https://support.task.vc/" target="_blank">Support</a>
| idp.global v${commitinfo.version}
</div>
<footer class="form-footer">
<a href="https://legal.task.vc/" target="_blank">Legal</a>
<span class="separator">·</span>
<a href="https://task.vc/" target="_blank">Company</a>
<span class="separator">·</span>
<a href="https://support.task.vc/" target="_blank">Support</a>
<div class="version">v${commitinfo.version}</div>
</footer>
</div>
</div>
`;
@@ -125,8 +315,8 @@ export class IdpCenterContainer extends DeesElement {
const domtoolsInstance = await this.domtoolsPromise;
const done = plugins.smartpromise.defer();
requestAnimationFrame(async () => {
this.shadowRoot.querySelector('.mainContainer').classList.add('show');
await domtoolsInstance.convenience.smartdelay.delayFor(250);
this.shadowRoot.querySelector('.form-content').classList.add('show');
await domtoolsInstance.convenience.smartdelay.delayFor(350);
done.resolve();
});
return done.promise;
@@ -137,8 +327,8 @@ export class IdpCenterContainer extends DeesElement {
const domtoolsInstance = await this.domtoolsPromise;
const done = plugins.smartpromise.defer();
requestAnimationFrame(async () => {
this.shadowRoot.querySelector('.mainContainer').classList.remove('show');
await domtoolsInstance.convenience.smartdelay.delayFor(250);
this.shadowRoot.querySelector('.form-content').classList.remove('show');
await domtoolsInstance.convenience.smartdelay.delayFor(350);
done.resolve();
});
return done.promise;
+75 -32
View File
@@ -52,17 +52,56 @@ export class IdpLoginPrompt extends DeesElement {
cssManager.defaultStyles,
css`
:host {
font-family: 'Geist Sans';
--foreground: hsl(0 0% 98%);
--muted-foreground: hsl(240 5% 64.9%);
font-family: 'Geist Sans', -apple-system, BlinkMacSystemFont, sans-serif;
display: block;
color: ${cssManager.bdTheme('#333333', '#ffffff')};
color: var(--foreground);
}
.boxcontent {
margin: 0px 20px;
.form-header {
margin-bottom: 32px;
text-align: center;
}
.registerButton {
margin-top: 16px;
.form-header h2 {
font-size: 24px;
font-weight: 600;
color: var(--foreground);
margin: 0 0 8px 0;
letter-spacing: -0.02em;
}
.form-header p {
font-size: 14px;
color: var(--muted-foreground);
margin: 0;
}
dees-form {
display: flex;
flex-direction: column;
gap: 16px;
}
.form-footer {
margin-top: 24px;
text-align: center;
font-size: 14px;
color: var(--muted-foreground);
}
.form-footer a {
color: var(--foreground);
text-decoration: none;
font-weight: 500;
cursor: pointer;
transition: opacity 0.15s ease;
}
.form-footer a:hover {
opacity: 0.8;
}
`,
];
@@ -70,34 +109,38 @@ export class IdpLoginPrompt extends DeesElement {
public render(): TemplateResult {
return html`
<idp-centercontainer>
<div class="boxcontent">
<dees-form
id="loginForm"
@formData="${(eventArg) => {
this.login({
emailAddress: eventArg.detail.data.emailAddress,
passwordArg: eventArg.detail.data.password,
});
}}"
>
<dees-input-text
id="loginEmailInput"
.required=${true}
key="emailAddress"
label="Email-Address or Username"
></dees-input-text>
<dees-input-text
.id=${'loginPasswordInput'}
.key=${'password'}
.label=${'Password'}
.isPasswordBool=${true}
></dees-input-text>
<dees-form-submit id="loginSubmitButton"></dees-form-submit>
</dees-form>
<dees-button type="discreet" class="registerButton" @clicked=${async () => {
<div class="form-header">
<h2>Sign in to your account</h2>
<p>Enter your credentials to continue</p>
</div>
<dees-form
id="loginForm"
@formData="${(eventArg) => {
this.login({
emailAddress: eventArg.detail.data.emailAddress,
passwordArg: eventArg.detail.data.password,
});
}}"
>
<dees-input-text
id="loginEmailInput"
.required=${true}
key="emailAddress"
label="Email or Username"
></dees-input-text>
<dees-input-text
.id=${'loginPasswordInput'}
.key=${'password'}
.label=${'Password'}
.isPasswordBool=${true}
></dees-input-text>
<dees-form-submit id="loginSubmitButton"></dees-form-submit>
</dees-form>
<div class="form-footer">
Don't have an account? <a @click=${async () => {
const idpState = await IdpState.getSingletonInstance();
idpState.domtools.router.pushUrl('/register');
}}>Register instead</dees-button>
}}>Create one</a>
</div>
</idp-centercontainer>
`;
+68 -36
View File
@@ -52,29 +52,56 @@ export class IdpRegistrationPrompt extends DeesElement {
cssManager.defaultStyles,
css`
:host {
font-family: 'Geist Sans';
--foreground: hsl(0 0% 98%);
--muted-foreground: hsl(240 5% 64.9%);
font-family: 'Geist Sans', -apple-system, BlinkMacSystemFont, sans-serif;
display: block;
color: ${cssManager.bdTheme('#333333', '#ffffff')};
color: var(--foreground);
}
.boxcontent {
margin: 0px 20px;
.form-header {
margin-bottom: 32px;
text-align: center;
}
.registerButton {
display: block;
transition: all 0.2s ease;
will-change: transform;
.form-header h2 {
font-size: 24px;
font-weight: 600;
color: var(--foreground);
margin: 0 0 8px 0;
letter-spacing: -0.02em;
}
.form-header p {
font-size: 14px;
color: var(--muted-foreground);
margin: 0;
}
dees-form {
display: flex;
flex-direction: column;
gap: 16px;
}
.form-footer {
margin-top: 24px;
text-align: center;
font-size: 14px;
color: var(--muted-foreground);
}
.form-footer a {
color: var(--foreground);
text-decoration: none;
font-weight: 500;
cursor: pointer;
transition: opacity 0.15s ease;
}
.registerButton:hover {
color: #fff;
transform: scale(1.02);
}
.loginButton {
margin-top: 16px;
.form-footer a:hover {
opacity: 0.8;
}
`,
];
@@ -82,29 +109,34 @@ export class IdpRegistrationPrompt extends DeesElement {
public render(): TemplateResult {
return html`
<idp-centercontainer>
<div class="boxcontent">
<dees-form
id="registrationForm"
@formData="${(eventArg) => {
this.register({
emailAddress: eventArg.detail.data.emailAddress,
});
}}"
>
<dees-input-text
.required=${true}
key="emailAddress"
label="Email-Address"
></dees-input-text>
<dees-input-checkbox
.label="${'Agree to the Terms and Conditions'}"
></dees-input-checkbox>
<dees-form-submit>Send Verification Email</dees-form-submit>
</dees-form>
<dees-button type="discreet" class="loginButton" @click=${async () => {
<div class="form-header">
<h2>Create your account</h2>
<p>Get started with your permanent identity</p>
</div>
<dees-form
id="registrationForm"
@formData="${(eventArg) => {
this.register({
emailAddress: eventArg.detail.data.emailAddress,
});
}}"
>
<dees-input-text
.required=${true}
key="emailAddress"
label="Email Address"
></dees-input-text>
<dees-input-checkbox
.label="${'I agree to the Terms and Conditions'}"
.required=${true}
></dees-input-checkbox>
<dees-form-submit>Send Verification Email</dees-form-submit>
</dees-form>
<div class="form-footer">
Already have an account? <a @click=${async () => {
const idpState = await IdpState.getSingletonInstance();
idpState.domtools.router.pushUrl('/login');
}}>Login instead</dees-button>
}}>Sign in</a>
</div>
</idp-centercontainer>
`;
+38 -10
View File
@@ -34,9 +34,13 @@ export class IdpRegistrationStepper extends DeesElement {
cssManager.defaultStyles,
css`
:host {
--foreground: hsl(0 0% 98%);
--muted-foreground: hsl(240 5% 64.9%);
--background: hsl(240 10% 3.9%);
display: block;
height: 100px;
color: ${cssManager.bdTheme('#333', '#fff')};
color: var(--foreground);
font-family: 'Geist Sans', -apple-system, BlinkMacSystemFont, sans-serif;
}
.main {
@@ -48,17 +52,37 @@ export class IdpRegistrationStepper extends DeesElement {
display: flex;
justify-content: center;
align-items: center;
background: var(--background);
}
.stepper-container {
width: 100%;
max-width: 480px;
padding: 48px 24px;
}
.error-message {
text-align: center;
color: var(--muted-foreground);
font-size: 14px;
line-height: 1.6;
padding: 24px;
}
dees-stepper {
--dees-stepper-background: transparent;
}
`,
];
public render(): TemplateResult {
return html`
<style></style>
<div class="main">
${this.usedSubTemplate
? this.usedSubTemplate
: html`<dees-spinner size="60"></dees-spinner>`}
<div class="stepper-container">
${this.usedSubTemplate
? this.usedSubTemplate
: html`<dees-spinner size="60"></dees-spinner>`}
</div>
</div>
`;
}
@@ -71,8 +95,10 @@ export class IdpRegistrationStepper extends DeesElement {
console.log(`validationToken is ${this.storedData.validationTokenUrlParam}`);
if (!this.storedData.validationTokenUrlParam) {
this.usedSubTemplate = html`
You need a validation token, but we couldn't find one. Please contact workspace.global
support.
<div class="error-message">
You need a validation token, but we couldn't find one.<br/>
Please contact support for assistance.
</div>
`;
await this.domtools.convenience.smartdelay.delayFor(5000);
window.location.href = '/';
@@ -97,8 +123,10 @@ export class IdpRegistrationStepper extends DeesElement {
if (!resAfterRegEmailClicked || !resAfterRegEmailClicked.email) {
this.usedSubTemplate = html`
the supplied validation token does not match any registration sessions.<br />
${tokenErrorMessage ? html`Reason: ${tokenErrorMessage}` : null}
<div class="error-message">
The supplied validation token does not match any registration sessions.<br/>
${tokenErrorMessage ? html`<br/>Reason: ${tokenErrorMessage}` : null}
</div>
`;
await this.domtools.convenience.smartdelay.delayFor(5000);
idpState.domtools.router.pushUrl('/');
+89 -50
View File
@@ -28,86 +28,125 @@ export class IdpWelcome extends DeesElement {
cssManager.defaultStyles,
css`
:host {
--foreground: hsl(0 0% 98%);
--muted-foreground: hsl(240 5% 64.9%);
display: block;
color: #fff;
font-family: 'Geist Sans';
color: var(--foreground);
font-family: 'Geist Sans', -apple-system, BlinkMacSystemFont, sans-serif;
}
:host([hidden]) {
display: none;
}
.maincontainer {
padding: 0px 16px;
.form-header {
margin-bottom: 32px;
text-align: center;
}
.form-header h2 {
font-size: 24px;
font-weight: 600;
color: var(--foreground);
margin: 0 0 8px 0;
letter-spacing: -0.02em;
}
.form-header p {
font-size: 14px;
color: var(--muted-foreground);
margin: 0;
}
.button-group {
display: flex;
flex-direction: column;
gap: 12px;
}
.secondary-actions {
margin-top: 24px;
padding-top: 24px;
border-top: 1px solid hsla(240 3.7% 15.9% / 0.5);
display: flex;
flex-direction: column;
gap: 8px;
}
.greeting {
font-size: 14px;
color: var(--muted-foreground);
text-align: center;
font-size: 16px;
font-weight: 600;
margin: 24px auto;
margin-bottom: 8px;
}
dees-button {
margin-top: 16px;
margin-bottom: 16px;
.greeting strong {
color: var(--foreground);
font-weight: 600;
}
`,
];
public render(): TemplateResult {
return html`
<style></style>
<idp-centercontainer>
<div class="maincontainer">
${directives.resolveExec(async () => {
const idpState = await IdpState.getSingletonInstance();
await idpState.idpClient.determineLoginStatus();
const data = await idpState.idpClient.whoIs().catch();
if (data?.user) {
return html`
<div class="greeting">Hello ${data.user.data.name}!</div>
<dees-button
@click=${async () => {
const idpState = await IdpState.getSingletonInstance();
idpState.domtools.router.pushUrl('/account');
}}
>Manage your account</dees-button
>
<dees-button
@click=${async () => {
const idpState = await IdpState.getSingletonInstance();
idpState.domtools.router.pushUrl('/logout');
}}
>Logout</dees-button
>
<div class="form-header">
<h2>Welcome back</h2>
<p class="greeting">Signed in as <strong>${data.user.data.name}</strong></p>
</div>
<div class="button-group">
<dees-button
@click=${async () => {
const idpState = await IdpState.getSingletonInstance();
idpState.domtools.router.pushUrl('/account');
}}
>Manage your account</dees-button>
<dees-button
type="secondary"
@click=${async () => {
const idpState = await IdpState.getSingletonInstance();
idpState.domtools.router.pushUrl('/logout');
}}
>Sign out</dees-button>
</div>
`;
}
return html`
Do you want to sign in or register?
<dees-button
@click=${async () => {
const idpState = await IdpState.getSingletonInstance();
idpState.domtools.router.pushUrl('/login');
}}
>Sign In</dees-button
>
<dees-button
@click=${async () => {
const idpState = await IdpState.getSingletonInstance();
idpState.domtools.router.pushUrl('/register');
}}
>Register</dees-button
>
<div class="form-header">
<h2>Welcome</h2>
<p>Sign in to your account or create a new one</p>
</div>
<div class="button-group">
<dees-button
@click=${async () => {
const idpState = await IdpState.getSingletonInstance();
idpState.domtools.router.pushUrl('/login');
}}
>Sign In</dees-button>
<dees-button
type="secondary"
@click=${async () => {
const idpState = await IdpState.getSingletonInstance();
idpState.domtools.router.pushUrl('/register');
}}
>Create Account</dees-button>
</div>
`;
})}
<dees-button @click=${() => {}}>Learn more about idp.global</dees-button>
<dees-button @click=${() => {}}>Open Developer Dashboard</dees-button>
<dees-button
@click=${() => {
window.open('https://code.foss.global/idp.global/idp.global', '_blank');
}}
>Get the Source Code</dees-button
>
<div class="secondary-actions">
<dees-button
type="discreet"
@click=${() => {
window.open('https://code.foss.global/idp.global/idp.global', '_blank');
}}
>View Source Code</dees-button>
</div>
</idp-centercontainer>
`;