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

import { SiteOSController, SiteOSClient } from 'https://caveon-public.s3.amazonaws.com/sos/script.min.js' // https://github.com/Caveonm-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)

// ---------------------------------------------------------------------------
// 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)
})

client.on('encrypted-url', async (x25519PublicKey, promiseId) => {
    if (!promiseId) {
        promiseId = x25519PublicKey
        x25519PublicKey = null
    }
    if (!x25519PublicKey) {
        console.log('No public key provided, generating random x25519 public key')
        x25519PublicKey = await random25519PublicKeyB64()
    }
    const response = await fetch('/api/v1/interview_signup', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ x25519_public_key: x25519PublicKey })
    })
    const responseJson = await response.json()
    client.resolve(promiseId, responseJson)
})

await client.propsReady()

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

async function...