Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions main/.mintlify/skills/acul-screen-generator/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ description: Generates complete, branded Auth0 Advanced Custom Universal Login (
license: Apache-2.0
metadata:
author: Auth0 <support@auth0.com>
openclaw:
emoji: "🔐"
homepage: https://github.com/auth0/agent-skills
---

# ACUL Screen Generator
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// ACUL JS — login-id screen boilerplate
// SDK: @auth0/auth0-acul-js
// Customize: apply design tokens, adjust layout, add/remove social providers

import LoginId from '@auth0/auth0-acul-js/login-id'

const manager = new LoginId()

function getIdentifierLabel() {
// Dynamic label based on configured identifiers
const ids = manager.screen.loginIdentifiers ?? []
if (ids.length === 0) return 'Email or username'
return `Enter your ${ids.join(' or ')}`
}

function renderSocialButtons() {
const connections = manager.transaction.alternateConnections ?? []
if (!connections.length) return ''

const buttons = connections.map(conn => `
<button
class="acul-social-btn"
data-connection="${conn.name}"
aria-label="Continue with ${conn.displayName}"
>
${conn.iconUrl ? `<img src="${conn.iconUrl}" alt="" width="20" height="20" />` : ''}
<span>Continue with ${conn.displayName}</span>
</button>
`).join('')

return `
<div class="acul-divider">
<span>${manager.screen.texts?.separatorText ?? 'Or'}</span>
</div>
<div class="acul-social-buttons">${buttons}</div>
`
}

function renderErrors() {
if (!manager.transaction.hasErrors) return ''
const msgs = manager.getErrors().map(e => `<p>${e.message}</p>`).join('')
return `<div class="acul-error-banner" role="alert">${msgs}</div>`
}

function render() {
const container = document.getElementById('app')
container.innerHTML = `
<div class="acul-page-wrapper">
<div class="acul-card">
<div class="acul-logo-slot"></div>

<h1 class="acul-heading">
${manager.screen.texts?.title ?? 'Log in'}
</h1>

${renderErrors()}

<form id="login-id-form" novalidate>
<div class="acul-field">
<label class="acul-label" for="username">
${manager.screen.texts?.usernameLabel ?? getIdentifierLabel()}
</label>
<input
id="username"
class="acul-input"
type="text"
name="username"
autocomplete="username"
required
/>
</div>

${manager.screen.isCaptchaAvailable ? `
<div class="acul-field">
<label class="acul-label" for="captcha">
${manager.screen.texts?.captchaLabel ?? 'Security code'}
</label>
<input id="captcha" class="acul-input" type="text" />
</div>
` : ''}

<button id="submit-btn" type="submit" class="acul-btn-primary">
${manager.screen.texts?.buttonText ?? 'Continue'}
</button>
</form>

${manager.screen.isPasskeyEnabled ? `
<button id="passkey-btn" class="acul-btn-secondary">
${manager.screen.texts?.passkeyButtonText ?? 'Use a passkey'}
</button>
` : ''}

${renderSocialButtons()}

<div class="acul-footer-links">
<a href="#">
${manager.screen.texts?.signupActionLinkText ?? "Don't have an account? Sign up"}
</a>
</div>
</div>
</div>
`

attachEventListeners()
}

function attachEventListeners() {
// Form submit
document.getElementById('login-id-form')?.addEventListener('submit', async (e) => {
e.preventDefault()
const submitBtn = document.getElementById('submit-btn')
submitBtn.disabled = true
submitBtn.textContent = 'Loading...'

const username = document.getElementById('username').value
const captcha = document.getElementById('captcha')?.value

await manager.login({ username, captcha: captcha || undefined })

submitBtn.disabled = false
submitBtn.textContent = manager.screen.texts?.buttonText ?? 'Continue'
})

// Passkey
document.getElementById('passkey-btn')?.addEventListener('click', async () => {
await manager.passkeyLogin()
})

// Social login
document.querySelectorAll('.acul-social-btn').forEach(btn => {
btn.addEventListener('click', async () => {
await manager.federatedLogin({ connection: btn.dataset.connection })
})
})
}

render()
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// ACUL JS — login-password screen boilerplate
// SDK: @auth0/auth0-acul-js
// Customize: apply design tokens, adjust layout

import LoginPassword from '@auth0/auth0-acul-js/login-password'

const manager = new LoginPassword()

function renderErrors() {
if (!manager.transaction.hasErrors) return ''
const msgs = manager.getErrors().map(e => `<p>${e.message}</p>`).join('')
return `<div class="acul-error-banner" role="alert">${msgs}</div>`
}

function renderSocialButtons() {
const connections = manager.transaction.alternateConnections ?? []
if (!connections.length) return ''

return `
<div class="acul-divider">
<span>${manager.screen.texts?.separatorText ?? 'Or'}</span>
</div>
${connections.map(conn => `
<button class="acul-social-btn" data-connection="${conn.name}">
${conn.iconUrl ? `<img src="${conn.iconUrl}" alt="" width="20" height="20" />` : ''}
<span>Continue with ${conn.displayName}</span>
</button>
`).join('')}
`
}

function render() {
const container = document.getElementById('app')
container.innerHTML = `
<div class="acul-page-wrapper">
<div class="acul-card">
<div class="acul-logo-slot"></div>

<h1 class="acul-heading">
${manager.screen.texts?.title ?? 'Enter your password'}
</h1>

${renderErrors()}

<form id="login-password-form" novalidate>
<div class="acul-field">
<label class="acul-label" for="password">
${manager.screen.texts?.passwordLabel ?? 'Password'}
</label>
<div class="acul-input-wrapper">
<input
id="password"
class="acul-input"
type="password"
name="password"
autocomplete="current-password"
required
/>
<button type="button" id="toggle-password" class="acul-input-toggle">
Show
</button>
</div>
</div>

${manager.screen.isCaptchaAvailable ? `
<div class="acul-field">
<label class="acul-label" for="captcha">
${manager.screen.texts?.captchaLabel ?? 'Security code'}
</label>
<input id="captcha" class="acul-input" type="text" />
</div>
` : ''}

<button id="submit-btn" type="submit" class="acul-btn-primary">
${manager.screen.texts?.buttonText ?? 'Log in'}
</button>
</form>

${manager.screen.isPasskeyEnabled ? `
<button id="passkey-btn" class="acul-btn-secondary">
${manager.screen.texts?.passkeyButtonText ?? 'Use a passkey instead'}
</button>
` : ''}

${renderSocialButtons()}

<div class="acul-footer-links">
<a href="#">
${manager.screen.texts?.forgotPasswordText ?? 'Forgot password?'}
</a>
</div>
</div>
</div>
`

attachEventListeners()
}

function attachEventListeners() {
document.getElementById('login-password-form')?.addEventListener('submit', async (e) => {
e.preventDefault()
const submitBtn = document.getElementById('submit-btn')
submitBtn.disabled = true
submitBtn.textContent = 'Loading...'

const password = document.getElementById('password').value
const captcha = document.getElementById('captcha')?.value

await manager.login({ password, captcha: captcha || undefined })

submitBtn.disabled = false
submitBtn.textContent = manager.screen.texts?.buttonText ?? 'Log in'
})

document.getElementById('toggle-password')?.addEventListener('click', () => {
const input = document.getElementById('password')
const btn = document.getElementById('toggle-password')
if (input.type === 'password') {
input.type = 'text'
btn.textContent = 'Hide'
} else {
input.type = 'password'
btn.textContent = 'Show'
}
})

document.getElementById('passkey-btn')?.addEventListener('click', async () => {
await manager.passkeyLogin()
})

document.querySelectorAll('.acul-social-btn').forEach(btn => {
btn.addEventListener('click', async () => {
await manager.federatedLogin({ connection: btn.dataset.connection })
})
})
}

render()
Loading