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
Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

+5 -1
View File
@@ -31,7 +31,11 @@
"user data", "user data",
"user sessions" "user sessions"
] ]
} },
"services": [
"mongodb",
"minio"
]
}, },
"npmci": { "npmci": {
"npmGlobalTools": [], "npmGlobalTools": [],
+247 -57
View File
@@ -32,89 +32,279 @@ export class IdpCenterContainer extends DeesElement {
cssManager.defaultStyles, cssManager.defaultStyles,
css` css`
:host { :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; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
background: var(--background);
} }
.mainContainer { .split-container {
position: absolute; position: absolute;
top: 0px; top: 0;
left: 0;
width: 100%; width: 100%;
height: 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; display: flex;
flex-direction: column;
justify-content: center; justify-content: center;
align-items: center; padding: 48px;
opacity: 0; position: relative;
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;
overflow: hidden; overflow: hidden;
} }
h1 { .brand-panel::before {
font-size: 24px; content: '';
font-family: 'Cal Sans'; position: absolute;
text-align: center; top: 0;
letter-spacing:0.0125em; 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 { .brand-content {
padding: 0px 0px 16px 0px; 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; text-align: center;
margin: auto;
color: ${cssManager.bdTheme('#666', '#ccc')};
font-size: 12px; font-size: 12px;
line-height: 100%; color: var(--muted-foreground);
padding: 8px;
background: ${cssManager.bdTheme('#f5f5f5', '#111')};
border-top: 1px solid ${cssManager.bdTheme('#ccc', '#222222')};
color: ${cssManager.bdTheme('#666', '#888')};
} }
.legalinfo a { .form-footer a {
color: ${cssManager.bdTheme('#666', '#ccc')}; color: var(--muted-foreground);
text-decoration: none; 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() { render() {
return html` return html`
<div class="mainContainer"> <div class="split-container">
<div class="loginblock"> <!-- Left: Branding Panel -->
<h1>idp.global</h1> <div class="brand-panel">
<div class="contentSpacer"> <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> <slot></slot>
</div> </div>
<div class="legalinfo"> <footer class="form-footer">
<a href="https://legal.task.vc/" target="_blank">Legal Info</a> <a href="https://legal.task.vc/" target="_blank">Legal</a>
| <a href="https://task.vc/" target="_blank">Company Website</a> <span class="separator">·</span>
| <a href="https://support.task.vc/" target="_blank">Support</a> <a href="https://task.vc/" target="_blank">Company</a>
| idp.global v${commitinfo.version} <span class="separator">·</span>
</div> <a href="https://support.task.vc/" target="_blank">Support</a>
<div class="version">v${commitinfo.version}</div>
</footer>
</div> </div>
</div> </div>
`; `;
@@ -125,8 +315,8 @@ export class IdpCenterContainer extends DeesElement {
const domtoolsInstance = await this.domtoolsPromise; const domtoolsInstance = await this.domtoolsPromise;
const done = plugins.smartpromise.defer(); const done = plugins.smartpromise.defer();
requestAnimationFrame(async () => { requestAnimationFrame(async () => {
this.shadowRoot.querySelector('.mainContainer').classList.add('show'); this.shadowRoot.querySelector('.form-content').classList.add('show');
await domtoolsInstance.convenience.smartdelay.delayFor(250); await domtoolsInstance.convenience.smartdelay.delayFor(350);
done.resolve(); done.resolve();
}); });
return done.promise; return done.promise;
@@ -137,8 +327,8 @@ export class IdpCenterContainer extends DeesElement {
const domtoolsInstance = await this.domtoolsPromise; const domtoolsInstance = await this.domtoolsPromise;
const done = plugins.smartpromise.defer(); const done = plugins.smartpromise.defer();
requestAnimationFrame(async () => { requestAnimationFrame(async () => {
this.shadowRoot.querySelector('.mainContainer').classList.remove('show'); this.shadowRoot.querySelector('.form-content').classList.remove('show');
await domtoolsInstance.convenience.smartdelay.delayFor(250); await domtoolsInstance.convenience.smartdelay.delayFor(350);
done.resolve(); done.resolve();
}); });
return done.promise; return done.promise;
+53 -10
View File
@@ -52,17 +52,56 @@ export class IdpLoginPrompt extends DeesElement {
cssManager.defaultStyles, cssManager.defaultStyles,
css` css`
:host { :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; display: block;
color: ${cssManager.bdTheme('#333333', '#ffffff')}; color: var(--foreground);
} }
.boxcontent { .form-header {
margin: 0px 20px; margin-bottom: 32px;
text-align: center;
} }
.registerButton { .form-header h2 {
margin-top: 16px; 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,7 +109,10 @@ export class IdpLoginPrompt extends DeesElement {
public render(): TemplateResult { public render(): TemplateResult {
return html` return html`
<idp-centercontainer> <idp-centercontainer>
<div class="boxcontent"> <div class="form-header">
<h2>Sign in to your account</h2>
<p>Enter your credentials to continue</p>
</div>
<dees-form <dees-form
id="loginForm" id="loginForm"
@formData="${(eventArg) => { @formData="${(eventArg) => {
@@ -84,7 +126,7 @@ export class IdpLoginPrompt extends DeesElement {
id="loginEmailInput" id="loginEmailInput"
.required=${true} .required=${true}
key="emailAddress" key="emailAddress"
label="Email-Address or Username" label="Email or Username"
></dees-input-text> ></dees-input-text>
<dees-input-text <dees-input-text
.id=${'loginPasswordInput'} .id=${'loginPasswordInput'}
@@ -94,10 +136,11 @@ export class IdpLoginPrompt extends DeesElement {
></dees-input-text> ></dees-input-text>
<dees-form-submit id="loginSubmitButton"></dees-form-submit> <dees-form-submit id="loginSubmitButton"></dees-form-submit>
</dees-form> </dees-form>
<dees-button type="discreet" class="registerButton" @clicked=${async () => { <div class="form-footer">
Don't have an account? <a @click=${async () => {
const idpState = await IdpState.getSingletonInstance(); const idpState = await IdpState.getSingletonInstance();
idpState.domtools.router.pushUrl('/register'); idpState.domtools.router.pushUrl('/register');
}}>Register instead</dees-button> }}>Create one</a>
</div> </div>
</idp-centercontainer> </idp-centercontainer>
`; `;
+52 -20
View File
@@ -52,29 +52,56 @@ export class IdpRegistrationPrompt extends DeesElement {
cssManager.defaultStyles, cssManager.defaultStyles,
css` css`
:host { :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; display: block;
color: ${cssManager.bdTheme('#333333', '#ffffff')}; color: var(--foreground);
} }
.boxcontent { .form-header {
margin: 0px 20px; margin-bottom: 32px;
text-align: center;
} }
.registerButton { .form-header h2 {
display: block; font-size: 24px;
transition: all 0.2s ease; font-weight: 600;
will-change: transform; 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; cursor: pointer;
transition: opacity 0.15s ease;
} }
.registerButton:hover { .form-footer a:hover {
color: #fff; opacity: 0.8;
transform: scale(1.02);
}
.loginButton {
margin-top: 16px;
} }
`, `,
]; ];
@@ -82,7 +109,10 @@ export class IdpRegistrationPrompt extends DeesElement {
public render(): TemplateResult { public render(): TemplateResult {
return html` return html`
<idp-centercontainer> <idp-centercontainer>
<div class="boxcontent"> <div class="form-header">
<h2>Create your account</h2>
<p>Get started with your permanent identity</p>
</div>
<dees-form <dees-form
id="registrationForm" id="registrationForm"
@formData="${(eventArg) => { @formData="${(eventArg) => {
@@ -94,17 +124,19 @@ export class IdpRegistrationPrompt extends DeesElement {
<dees-input-text <dees-input-text
.required=${true} .required=${true}
key="emailAddress" key="emailAddress"
label="Email-Address" label="Email Address"
></dees-input-text> ></dees-input-text>
<dees-input-checkbox <dees-input-checkbox
.label="${'Agree to the Terms and Conditions'}" .label="${'I agree to the Terms and Conditions'}"
.required=${true}
></dees-input-checkbox> ></dees-input-checkbox>
<dees-form-submit>Send Verification Email</dees-form-submit> <dees-form-submit>Send Verification Email</dees-form-submit>
</dees-form> </dees-form>
<dees-button type="discreet" class="loginButton" @click=${async () => { <div class="form-footer">
Already have an account? <a @click=${async () => {
const idpState = await IdpState.getSingletonInstance(); const idpState = await IdpState.getSingletonInstance();
idpState.domtools.router.pushUrl('/login'); idpState.domtools.router.pushUrl('/login');
}}>Login instead</dees-button> }}>Sign in</a>
</div> </div>
</idp-centercontainer> </idp-centercontainer>
`; `;
+35 -7
View File
@@ -34,9 +34,13 @@ export class IdpRegistrationStepper extends DeesElement {
cssManager.defaultStyles, cssManager.defaultStyles,
css` css`
:host { :host {
--foreground: hsl(0 0% 98%);
--muted-foreground: hsl(240 5% 64.9%);
--background: hsl(240 10% 3.9%);
display: block; display: block;
height: 100px; color: var(--foreground);
color: ${cssManager.bdTheme('#333', '#fff')}; font-family: 'Geist Sans', -apple-system, BlinkMacSystemFont, sans-serif;
} }
.main { .main {
@@ -48,18 +52,38 @@ export class IdpRegistrationStepper extends DeesElement {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: 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 { public render(): TemplateResult {
return html` return html`
<style></style>
<div class="main"> <div class="main">
<div class="stepper-container">
${this.usedSubTemplate ${this.usedSubTemplate
? this.usedSubTemplate ? this.usedSubTemplate
: html`<dees-spinner size="60"></dees-spinner>`} : html`<dees-spinner size="60"></dees-spinner>`}
</div> </div>
</div>
`; `;
} }
@@ -71,8 +95,10 @@ export class IdpRegistrationStepper extends DeesElement {
console.log(`validationToken is ${this.storedData.validationTokenUrlParam}`); console.log(`validationToken is ${this.storedData.validationTokenUrlParam}`);
if (!this.storedData.validationTokenUrlParam) { if (!this.storedData.validationTokenUrlParam) {
this.usedSubTemplate = html` this.usedSubTemplate = html`
You need a validation token, but we couldn't find one. Please contact workspace.global <div class="error-message">
support. 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); await this.domtools.convenience.smartdelay.delayFor(5000);
window.location.href = '/'; window.location.href = '/';
@@ -97,8 +123,10 @@ export class IdpRegistrationStepper extends DeesElement {
if (!resAfterRegEmailClicked || !resAfterRegEmailClicked.email) { if (!resAfterRegEmailClicked || !resAfterRegEmailClicked.email) {
this.usedSubTemplate = html` this.usedSubTemplate = html`
the supplied validation token does not match any registration sessions.<br /> <div class="error-message">
${tokenErrorMessage ? html`Reason: ${tokenErrorMessage}` : null} 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); await this.domtools.convenience.smartdelay.delayFor(5000);
idpState.domtools.router.pushUrl('/'); idpState.domtools.router.pushUrl('/');
+65 -26
View File
@@ -28,86 +28,125 @@ export class IdpWelcome extends DeesElement {
cssManager.defaultStyles, cssManager.defaultStyles,
css` css`
:host { :host {
--foreground: hsl(0 0% 98%);
--muted-foreground: hsl(240 5% 64.9%);
display: block; display: block;
color: #fff; color: var(--foreground);
font-family: 'Geist Sans'; font-family: 'Geist Sans', -apple-system, BlinkMacSystemFont, sans-serif;
} }
:host([hidden]) { :host([hidden]) {
display: none; display: none;
} }
.maincontainer { .form-header {
padding: 0px 16px; 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 { .greeting {
font-size: 14px;
color: var(--muted-foreground);
text-align: center; text-align: center;
font-size: 16px; margin-bottom: 8px;
font-weight: 600;
margin: 24px auto;
} }
dees-button { .greeting strong {
margin-top: 16px; color: var(--foreground);
margin-bottom: 16px; font-weight: 600;
} }
`, `,
]; ];
public render(): TemplateResult { public render(): TemplateResult {
return html` return html`
<style></style>
<idp-centercontainer> <idp-centercontainer>
<div class="maincontainer">
${directives.resolveExec(async () => { ${directives.resolveExec(async () => {
const idpState = await IdpState.getSingletonInstance(); const idpState = await IdpState.getSingletonInstance();
await idpState.idpClient.determineLoginStatus(); await idpState.idpClient.determineLoginStatus();
const data = await idpState.idpClient.whoIs().catch(); const data = await idpState.idpClient.whoIs().catch();
if (data?.user) { if (data?.user) {
return html` return html`
<div class="greeting">Hello ${data.user.data.name}!</div> <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 <dees-button
@click=${async () => { @click=${async () => {
const idpState = await IdpState.getSingletonInstance(); const idpState = await IdpState.getSingletonInstance();
idpState.domtools.router.pushUrl('/account'); idpState.domtools.router.pushUrl('/account');
}} }}
>Manage your account</dees-button >Manage your account</dees-button>
>
<dees-button <dees-button
type="secondary"
@click=${async () => { @click=${async () => {
const idpState = await IdpState.getSingletonInstance(); const idpState = await IdpState.getSingletonInstance();
idpState.domtools.router.pushUrl('/logout'); idpState.domtools.router.pushUrl('/logout');
}} }}
>Logout</dees-button >Sign out</dees-button>
> </div>
`; `;
} }
return html` return html`
Do you want to sign in or register? <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 <dees-button
@click=${async () => { @click=${async () => {
const idpState = await IdpState.getSingletonInstance(); const idpState = await IdpState.getSingletonInstance();
idpState.domtools.router.pushUrl('/login'); idpState.domtools.router.pushUrl('/login');
}} }}
>Sign In</dees-button >Sign In</dees-button>
>
<dees-button <dees-button
type="secondary"
@click=${async () => { @click=${async () => {
const idpState = await IdpState.getSingletonInstance(); const idpState = await IdpState.getSingletonInstance();
idpState.domtools.router.pushUrl('/register'); idpState.domtools.router.pushUrl('/register');
}} }}
>Register</dees-button >Create Account</dees-button>
> </div>
`; `;
})} })}
<dees-button @click=${() => {}}>Learn more about idp.global</dees-button> <div class="secondary-actions">
<dees-button @click=${() => {}}>Open Developer Dashboard</dees-button>
<dees-button <dees-button
type="discreet"
@click=${() => { @click=${() => {
window.open('https://code.foss.global/idp.global/idp.global', '_blank'); window.open('https://code.foss.global/idp.global/idp.global', '_blank');
}} }}
>Get the Source Code</dees-button >View Source Code</dees-button>
>
</div> </div>
</idp-centercontainer> </idp-centercontainer>
`; `;