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)))
       ...