Create a responsive layout with a fixed main container using SiteOSController and SiteOSClient from the provided script.
by Rich Costello
HTML
<div id="shell">
<header id="header">
<h1>Caveon</h1>
</header>
<main id="main"></main>
</div>
CSS
*,
*::before,
*::after {
box-sizing: border-box;
}
html,
body {
height: 100%;
margin: 0;
padding: 0;
font-family: system-ui, sans-serif;
}
#shell {
position: fixed;
inset: 0;
display: flex;
flex-direction: column;
}
#header {
flex-shrink: 0;
padding: 0 1rem;
background: #1a1a2e;
color: #fff;
display: flex;
align-items: center;
height: 3rem;
}
#header h1 {
margin: 0;
font-size: 1.125rem;
font-weight: 600;
letter-spacing: 0.05em;
}
#main {
flex: 1;
min-height: 0;
position: relative;
}
/* SiteOS mounts an iframe into #main — make it fill the container */
#main iframe {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
border: none;
}
/* Tablet and up: add side gutters so the app doesn't stretch too wide */
@media (min-width: 768px) {
#shell {
max-width: 900px;
margin-inline: auto;
/* keep the fixed behaviour relative to the centred column */
left: 50%;
right: auto;
transform: translateX(-50%);
width: 100%;
box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.08);
}
}
JavaScript
;(async () => {
const { SiteOSController, SiteOSClient } = await import('https://caveon-public.s3.amazonaws.com/sos/script.min.js') // https://github.com/Caveon-LLC/sos
// ---------------------------------------------------------------------------
// Controller — runs on the host page, mounts SiteOS into #main
// ---------------------------------------------------------------------------
const mainController = new SiteOSController('https://pub.caveon.com/interview_signup')
await mainController.launch('main')
const welcomeMessage = await mainController.request('welcome-message')
console.log(welcomeMessage)
const { publicKeyB64, privateKey } = await generateX25519KeyPair()
const encryptedData = await mainController.request('encrypted-url', publicKeyB64)
const decryptedUrl = await decryptSignupUrl(encryptedData, privateKey)
console.log('Signup URL:', decryptedUrl)
// ---------------------------------------------------------------------------
// Client — runs inside the SiteOS iframe, handles requests from the controller
// ---------------------------------------------------------------------------
const client = new SiteOSClient()
client.on('welcome-message', async (promiseId) => {
const message = 'Welcome! Fork this fiddle and use it to get and decrypt the interview signup URL'
client.resolve(promiseId, message)
})
await client.propsReady()
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
async function generateX25519KeyPair() {
const keyPair = await crypto.subtle.generateKey({ name: 'X25519' }, true, ['deriveBits'])
const rawPub = await crypto.subtle.exportKey('raw', keyPair.publicKey)
const publicKeyB64 = btoa(String.fromCharCode(...new Uint8Array(rawPub)))
...