Skip to main content

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Responsive Sleep Diary</title>
  <style>
    #sleep-diary table {
      width: 100%;
      border-collapse: collapse;
      font-family: sans-serif;
      margin-bottom: 40px;
    }
    #sleep-diary th, #sleep-diary td {
      padding: 8px;
      text-align: center;
      vertical-align: top;
    }
    .timeline {
      display: flex;
      flex-wrap: wrap;
      gap: 2px;
      justify-content: center;
    }
    .hour-cell {
      width: 36px;
      height: 36px;
      border: 1px solid #ccc;
      font-size: 10px;
      text-align: left;
      line-height: 1;
      position: relative;
      padding: 2px;
      cursor: pointer;
      overflow: hidden;
    }
    .time-label {
      font-size: 8px;
      position: absolute;
      top: 2px;
      left: 2px;
      opacity: 0.6;
    }
    .hour-cell:focus {
      outline: 2px solid #0077cc;
    }
    textarea, input {
      width: 100%;
      box-sizing: border-box;
    }
    .legend-box {
      background: #f9f9f9;
      border: 1px solid #ddd;
      padding: 10px;
      margin-top: -30px;
      margin-bottom: 40px;
      font-size: 14px;
      text-align: left;
    }
    .legend-box ul {
      list-style: none;
      padding: 0;
      margin: 0;
      columns: 2;
    }
    .legend-box li {
      margin-bottom: 4px;
    }
    @media screen and (max-width: 768px) {
      #sleep-diary table, #sleep-diary thead, #sleep-diary tbody, #sleep-diary th, #sleep-diary td, #sleep-diary tr {
        display: block;
        width: 100%;
      }
      #sleep-diary thead tr {
        display: none;
      }
      #sleep-diary td {
        margin-bottom: 12px;
        text-align: left;
        position: relative;
        padding-left: 50%;
      }
      #sleep-diary td::before {
        position: absolute;
        top: 8px;
        left: 8px;
        width: 45%;
        padding-right: 10px;
        white-space: nowrap;
        font-weight: bold;
        content: attr(data-label);
      }
      .timeline {
        justify-content: flex-start;
        overflow-x: auto;
        white-space: nowrap;
      }
      .hour-cell {
        flex: 0 0 auto;
      }
    }
  </style>
</head>
<body>
  <div id="sleep-diary">
    <h2>Multi-Week Sleep Diary</h2>
    <p>Please complete each row daily. Use the legend to mark activities in the timeline.</p>

    <label for="weeks">Number of weeks:</label>
    <input type="number" id="weeks" value="2" min="1" max="12" onchange="renderDiary()" />

    <div id="diary-tables"></div>

    <button onclick="downloadDiary()">Download Diary (CSV)</button>
  </div>

  <script>
    const hours = ['6pm','7pm','8pm','9pm','10pm','11pm','12am','1am','2am','3am','4am','5am','6am','7am','8am','9am','10am','11am','12pm','1pm','2pm','3pm','4pm','5pm','6pm'];
    const days = ['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'];

    function renderDiary() {
      const weekCount = parseInt(document.getElementById("weeks").value);
      const container = document.getElementById("diary-tables");
      container.innerHTML = '';
      for (let w = 1; w <= weekCount; w++) {
        const table = document.createElement("table");
        table.innerHTML = `
          <thead>
            <tr><th colspan="6">Week ${w}</th></tr>
            <tr>
              <th>Day<br><small>Date below</small></th>
              <th>Lights Out Time</th>
              <th>Total Sleep (hrs)</th>
              <th>Mood</th>
              <th>Notes</th>
              <th>Sleep & Activity Timeline<br><small>(6pm to 6pm)</small></th>
            </tr>
          </thead>
          <tbody>
            ${days.map(day => `
              <tr>
                <td data-label="Day">${day}<br><input type="date"></td>
                <td data-label="Lights Out Time"><input type="time"></td>
                <td data-label="Total Sleep (hrs)"><input type="number" step="0.1" min="0" max="24"></td>
                <td data-label="Mood"><input type="text" placeholder="Mood"></td>
                <td data-label="Notes"><textarea rows="2" cols="20" placeholder="Notes..."></textarea></td>
                <td data-label="Timeline"><div class="timeline">${hours.map(h => `<div class='hour-cell' title='${h}' contenteditable='true'><span class='time-label'>${h}</span></div>`).join('')}</div></td>
              </tr>
            `).join('')}
          </tbody>
        `;
        container.appendChild(table);

        const legend = document.createElement("p");
        legend.innerHTML = `
          <div class="legend-box">
            <strong>Activity Legend:</strong>
            <ul>
              <li><strong>A</strong> = Alcohol</li>
              <li><strong>C</strong> = Caffeine</li>
              <li><strong>P</strong> = Pills (sleep aids)</li>
              <li><strong>M</strong> = Meal</li>
              <li><strong>S</strong> = Snack</li>
              <li><strong>X</strong> = Exercise</li>
              <li><strong>T</strong> = Toilet</li>
              <li><strong>N</strong> = Noise disturbance</li>
              <li><strong>W</strong> = Wake alarm</li>
            </ul>
          </div>`;
        container.appendChild(legend);
      }
    }

    function downloadDiary() {
      const tables = document.querySelectorAll("#diary-tables table");
      const rows = [];
      tables.forEach((table, weekIndex) => {
        const weekRows = Array.from(table.querySelectorAll("tbody tr")).map(tr => {
          const inputs = Array.from(tr.querySelectorAll("input, textarea"));
          const timeline = Array.from(tr.querySelectorAll(".hour-cell")).map(cell => cell.innerText.replace(/\s*(6pm|7pm|8pm|9pm|10pm|11pm|12am|1am|2am|3am|4am|5am|6am|7am|8am|9am|10am|11am|12pm|1pm|2pm|3pm|4pm|5pm|6pm)/g, '').trim()).join(' | ');
          return [`Week ${weekIndex + 1}`, days[weekRows.length % 7], ...inputs.map(el => el.value || ''), timeline];
        });
        rows.push(...weekRows);
      });

      const csv = ['Week,Day,Date,Lights Out,Total Sleep,Mood,Notes,Timeline']
        .concat(rows.map(r => r.join(",")))
        .join("\n");
      const blob = new Blob([csv], { type: 'text/csv' });
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = 'sleep-diary.csv';
      a.click();
      URL.revokeObjectURL(url);
    }

    renderDiary();
  </script>
</body>
</html>
<!-- wp:code -->
<pre class="wp-block-code"><code>
&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
  &lt;meta charset="UTF-8"&gt;
  &lt;title&gt;Responsive Sleep Diary&lt;/title&gt;
  &lt;style&gt;
    #sleep-diary table {
      width: 100%;
      border-collapse: collapse;
      font-family: sans-serif;
      margin-bottom: 40px;
    }
    #sleep-diary th, #sleep-diary td {
      padding: 8px;
      text-align: center;
      vertical-align: top;
    }
    .timeline {
      display: flex;
      flex-wrap: wrap;
      gap: 2px;
      justify-content: center;
    }
    .hour-cell {
      width: 36px;
      height: 36px;
      border: 1px solid #ccc;
      font-size: 10px;
      text-align: left;
      line-height: 1;
      position: relative;
      padding: 2px;
      cursor: pointer;
      overflow: hidden;
    }
    .time-label {
      font-size: 8px;
      position: absolute;
      top: 2px;
      left: 2px;
      opacity: 0.6;
    }
    .hour-cell:focus {
      outline: 2px solid #0077cc;
    }
    textarea, input {
      width: 100%;
      box-sizing: border-box;
    }
    .legend-box {
      background: #f9f9f9;
      border: 1px solid #ddd;
      padding: 10px;
      margin-top: -30px;
      margin-bottom: 40px;
      font-size: 14px;
      text-align: left;
    }
    .legend-box ul {
      list-style: none;
      padding: 0;
      margin: 0;
      columns: 2;
    }
    .legend-box li {
      margin-bottom: 4px;
    }
    @media screen and (max-width: 768px) {
      #sleep-diary table, #sleep-diary thead, #sleep-diary tbody, #sleep-diary th, #sleep-diary td, #sleep-diary tr {
        display: block;
        width: 100%;
      }
      #sleep-diary thead tr {
        display: none;
      }
      #sleep-diary td {
        margin-bottom: 12px;
        text-align: left;
        position: relative;
        padding-left: 50%;
      }
      #sleep-diary td::before {
        position: absolute;
        top: 8px;
        left: 8px;
        width: 45%;
        padding-right: 10px;
        white-space: nowrap;
        font-weight: bold;
        content: attr(data-label);
      }
      .timeline {
        justify-content: flex-start;
        overflow-x: auto;
        white-space: nowrap;
      }
      .hour-cell {
        flex: 0 0 auto;
      }
    }
  &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;div id="sleep-diary"&gt;
    &lt;h2&gt;Multi-Week Sleep Diary&lt;/h2&gt;
    &lt;p&gt;Please complete each row daily. Use the legend to mark activities in the timeline.&lt;/p&gt;

    &lt;label for="weeks"&gt;Number of weeks:&lt;/label&gt;
    &lt;input type="number" id="weeks" value="2" min="1" max="12" onchange="renderDiary()" /&gt;

    &lt;div id="diary-tables"&gt;&lt;/div&gt;

    &lt;button onclick="downloadDiary()"&gt;Download Diary (CSV)&lt;/button&gt;
  &lt;/div&gt;

  &lt;script&gt;
    const hours = &#91;'6pm','7pm','8pm','9pm','10pm','11pm','12am','1am','2am','3am','4am','5am','6am','7am','8am','9am','10am','11am','12pm','1pm','2pm','3pm','4pm','5pm','6pm'];
    const days = &#91;'Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'];

    function renderDiary() {
      const weekCount = parseInt(document.getElementById("weeks").value);
      const container = document.getElementById("diary-tables");
      container.innerHTML = '';
      for (let w = 1; w &lt;= weekCount; w++) {
        const table = document.createElement("table");
        table.innerHTML = `
          &lt;thead&gt;
            &lt;tr&gt;&lt;th colspan="6"&gt;Week ${w}&lt;/th&gt;&lt;/tr&gt;
            &lt;tr&gt;
              &lt;th&gt;Day&lt;br&gt;&lt;small&gt;Date below&lt;/small&gt;&lt;/th&gt;
              &lt;th&gt;Lights Out Time&lt;/th&gt;
              &lt;th&gt;Total Sleep (hrs)&lt;/th&gt;
              &lt;th&gt;Mood&lt;/th&gt;
              &lt;th&gt;Notes&lt;/th&gt;
              &lt;th&gt;Sleep &amp; Activity Timeline&lt;br&gt;&lt;small&gt;(6pm to 6pm)&lt;/small&gt;&lt;/th&gt;
            &lt;/tr&gt;
          &lt;/thead&gt;
          &lt;tbody&gt;
            ${days.map(day =&gt; `
              &lt;tr&gt;
                &lt;td data-label="Day"&gt;${day}&lt;br&gt;&lt;input type="date"&gt;&lt;/td&gt;
                &lt;td data-label="Lights Out Time"&gt;&lt;input type="time"&gt;&lt;/td&gt;
                &lt;td data-label="Total Sleep (hrs)"&gt;&lt;input type="number" step="0.1" min="0" max="24"&gt;&lt;/td&gt;
                &lt;td data-label="Mood"&gt;&lt;input type="text" placeholder="Mood"&gt;&lt;/td&gt;
                &lt;td data-label="Notes"&gt;&lt;textarea rows="2" cols="20" placeholder="Notes..."&gt;&lt;/textarea&gt;&lt;/td&gt;
                &lt;td data-label="Timeline"&gt;&lt;div class="timeline"&gt;${hours.map(h =&gt; `&lt;div class='hour-cell' title='${h}' contenteditable='true'&gt;&lt;span class='time-label'&gt;${h}&lt;/span&gt;&lt;/div&gt;`).join('')}&lt;/div&gt;&lt;/td&gt;
              &lt;/tr&gt;
            `).join('')}
          &lt;/tbody&gt;
        `;
        container.appendChild(table);

        const legend = document.createElement("p");
        legend.innerHTML = `
          &lt;div class="legend-box"&gt;
            &lt;strong&gt;Activity Legend:&lt;/strong&gt;
            &lt;ul&gt;
              &lt;li&gt;&lt;strong&gt;A&lt;/strong&gt; = Alcohol&lt;/li&gt;
              &lt;li&gt;&lt;strong&gt;C&lt;/strong&gt; = Caffeine&lt;/li&gt;
              &lt;li&gt;&lt;strong&gt;P&lt;/strong&gt; = Pills (sleep aids)&lt;/li&gt;
              &lt;li&gt;&lt;strong&gt;M&lt;/strong&gt; = Meal&lt;/li&gt;
              &lt;li&gt;&lt;strong&gt;S&lt;/strong&gt; = Snack&lt;/li&gt;
              &lt;li&gt;&lt;strong&gt;X&lt;/strong&gt; = Exercise&lt;/li&gt;
              &lt;li&gt;&lt;strong&gt;T&lt;/strong&gt; = Toilet&lt;/li&gt;
              &lt;li&gt;&lt;strong&gt;N&lt;/strong&gt; = Noise disturbance&lt;/li&gt;
              &lt;li&gt;&lt;strong&gt;W&lt;/strong&gt; = Wake alarm&lt;/li&gt;
            &lt;/ul&gt;
          &lt;/div&gt;`;
        container.appendChild(legend);
      }
    }

    function downloadDiary() {
      const tables = document.querySelectorAll("#diary-tables table");
      const rows = &#91;];
      tables.forEach((table, weekIndex) =&gt; {
        const weekRows = Array.from(table.querySelectorAll("tbody tr")).map(tr =&gt; {
          const inputs = Array.from(tr.querySelectorAll("input, textarea"));
          const timeline = Array.from(tr.querySelectorAll(".hour-cell")).map(cell =&gt; cell.innerText.replace(/\s*(6pm|7pm|8pm|9pm|10pm|11pm|12am|1am|2am|3am|4am|5am|6am|7am|8am|9am|10am|11am|12pm|1pm|2pm|3pm|4pm|5pm|6pm)/g, '').trim()).join(' | ');
          return &#91;`Week ${weekIndex + 1}`, days&#91;weekRows.length % 7], ...inputs.map(el =&gt; el.value || ''), timeline];
        });
        rows.push(...weekRows);
      });

      const csv = &#91;'Week,Day,Date,Lights Out,Total Sleep,Mood,Notes,Timeline']
        .concat(rows.map(r =&gt; r.join(",")))
        .join("\n");
      const blob = new Blob(&#91;csv], { type: 'text/csv' });
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = 'sleep-diary.csv';
      a.click();
      URL.revokeObjectURL(url);
    }

    renderDiary();
  &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<!-- /wp:code -->