Skip to content
Open
1 change: 1 addition & 0 deletions css/components/_pretext.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ $navbar-breakpoint: 800px !default;
@use 'spacing';
@use 'elements/lists';
@use 'elements/fillin';
@use 'elements/veil';
@use 'elements/headings';
@use 'elements/links';
@use 'elements/tables';
Expand Down
111 changes: 111 additions & 0 deletions css/components/elements/_veil.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/* ==========================================================================
PreTeXt <veil> — final SCSS (light: royal blue, dark: burnt orange)
- Transparent background for revealed content
- Non-transparent button background (mode-aware)
- Works for inline (.veil--inline) and block (.veil--block) veils
- Includes keyboard focus and pressed feedback
========================================================================== */

/* ---------------- Theme tokens via CSS variables ------------------------- */
:root {
--veil-primary: #1d4ed8; /* royal blue */
--veil-btn-bg: #f8fafc; /* very light gray-blue */
--veil-text-color: inherit;

--veil-focus-ring: rgba(29, 78, 216, 0.35);
--veil-hover-ring: rgba(29, 78, 216, 0.18);
--veil-pressed-shadow: rgba(0,0,0,0.06);
}

/* Dark mode uses a burnt orange accent; PreTeXt toggles .dark-mode on <html> */
.dark-mode {
--veil-primary: #f59e0b; /* burnt orange */
--veil-btn-bg: rgba(245, 158, 11, 0.12);
--veil-text-color: inherit;

--veil-focus-ring: rgba(245, 158, 11, 0.45);
--veil-hover-ring: rgba(245, 158, 11, 0.22);
--veil-pressed-shadow: rgba(245, 158, 11, 0.16);
}

/* ---------------- Containers -------------------------------------------- */
.veil--inline { display: inline; }
.veil--block { margin: 0.25rem 0; }

/* ---------------- Reveal button (shown when hidden) --------------------- */
.veil .veil-toggle {
display: inline-block;
background: var(--veil-btn-bg);
color: var(--veil-primary);
padding: 0.06rem 0.35rem;
// border: 1px solid color-mix(in srgb, var(--veil-primary) 35%, transparent);
border: 1px solid currentColor;
border-radius: 0.25rem;
font-size: 0.85rem;
line-height: 1.2;
cursor: pointer;
vertical-align: baseline;
transition: box-shadow 120ms ease, transform 60ms ease, border-color 120ms ease;

&:hover {
box-shadow: 0 0 0 2px var(--veil-hover-ring);
}
&:active {
transform: scale(0.995);
}
&:focus-visible {
outline: 2px solid var(--veil-focus-ring);
outline-offset: 2px;
}
}

/* ---------------- Hidden by default ------------------------------------ */
.veil .veil-content {
display: none;
}

/* ---------------- Revealed state --------------------------------------- */
.veil.revealed .veil-toggle { display: none; }

.veil.revealed .veil-content {
display: inline; /* overridden to block for block variant */
background: transparent; /* transparent background as requested */
color: var(--veil-text-color);
border: 1px solid var(--veil-primary);
padding: 0.06rem 0.30rem;
border-radius: 0.20rem;
cursor: pointer;
transition: transform 60ms ease, box-shadow 120ms ease, border-color 120ms ease;

&:hover {
box-shadow: 0 0 0 2px var(--veil-hover-ring) inset;
}
&:focus-visible {
outline: 2px solid var(--veil-focus-ring);
outline-offset: 2px;
}

/* Pressed feedback (toggled by JS on pointer events) */
&.is-pressed {
transform: scale(0.995);
box-shadow: 0 0 0 3px var(--veil-pressed-shadow) inset;
}
}

/* Block variant shows content as a block when revealed */
.veil--block.revealed .veil-content {
display: block;
padding: 0.40rem 0.55rem;
}

/* ---------------- Print (HTML -> paper) --------------------------------- */
@media print {
.veil .veil-toggle { display: none !important; }
.veil .veil-content {
display: inline !important;
border: none !important;
background: transparent !important;
padding: 0 !important;
}
.veil--block .veil-content { display: block !important; }
}
100 changes: 100 additions & 0 deletions examples/sample-article/sample-article.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10215,6 +10215,106 @@ along with MathBook XML. If not, see <http://www.gnu.org/licenses/>.
</subsection>
</section>

<section xml:id="section-veil-testing">
<title>Veil Element Testing</title>

<p>The function <veil><m>f(t) = e^{-t}</m></veil> satisfies the differential equation <me>f'(t) = -f(t)</me>, which is characteristic of <veil>exponential</veil> decay.</p>

<p>This sentence contains a <veil>simple hidden phrase</veil> and continues normally.</p>

<p>Here is a veil wrapping <veil><em>emphasized hidden text</em></veil> within an emphasized span.</p>

<p>You can also hide <veil>inline math like <m>f(t) = e^{-t}</m></veil> without breaking the flow.</p>

<p>
Testing long text. Looks bad in print, but should be fine in HTML:
<veil>
This is a very long phrase that includes <em>emphasis</em>,
inline math <m>\int_0^1 x^2 dx = \frac{1}{3}</m>, and even a short sentence to see if line wrapping behaves properly.
</veil>
</p>

<p>
Testing long text inside <c>&lt;p&gt;</c> tags:
<veil>
<p>
This is a very long phrase that includes <em>emphasis</em>,
inline math <m>\int_0^1 x^2 dx = \frac{1}{3}</m>, and even a short sentence to see if line wrapping behaves properly.
</p>
</veil>
</p>

<p>Multiple consecutive veils: <veil>first hidden</veil>, then some regular text, and <veil>second hidden part</veil>.</p>

<veil><me>h(x) = \int_0^x \sin(t)\ dt</me></veil>

<p>A veil at the <veil>end of the sentence</veil>.</p>

<p>A hidden external link: <veil><url href="https://pretextbook.org">PreTeXt website</url></veil> is a great resource.</p>

<p>The command is <veil><c>pip install pretext</c></veil>, which installs the toolkit.</p>

<p>Two quick reveals: <veil>first</veil>,<veil>second</veil>.</p>

<p>The derivative of <m>e^{kt}</m> is <veil><m>k\,e^{kt}</m></veil>.</p>

<p>The identity <m>\sin^2\theta + \cos^2\theta =</m> <veil><m>1</m></veil> is fundamental.</p>

<ul>
<li>The capital of France is <veil>Paris</veil>.</li>
<li><m>\int_0^1 x^2\,dx =</m> <veil><m>\tfrac{1}{3}</m></veil>.</li>
</ul>

<ol>
<li>Differentiate: <veil><m>\frac{d}{dx}\big(x^3\big)=3x^2</m></veil></li>
<li>Evaluate: <m>f(2) = </m> <veil><m>12</m></veil></li>
</ol>

<table>
<title>A tiny table with veiled entries</title>
<tabular>
<row>
<cell><em>Quantity</em></cell>
<cell><em>Value</em></cell>
</row>
<row>
<cell>Slope</cell>
<cell><veil><m>\frac{\Delta y}{\Delta x}</m></veil></cell>
</row>
<row>
<cell>Secret code</cell>
<cell><veil><c>AX-42</c></veil></cell>
</row>
</tabular>
</table>

<figure>
<caption>Just some <veil>cubic</veil> functions.</caption>
<image source="images/cubic-function.png" />
</figure>

<veil>
<p><term>Hint.</term> Use an integrating factor <m>\mu(x)=e^{\int p(x)\,dx}</m> to solve <m>y'+p(x)y=q(x)</m>.</p>
</veil>

<veil>
<me>\int_0^{2\pi}\sin(x)\,dx = 0</me>
</veil>

<theorem>
<title>Geometric Series</title>
<statement>
For <m>|r| &lt; 1</m>, <m>\sum_{k=0}^{\infty} r^k =</m> <veil><m>\frac{1}{1-r}</m></veil>.
</statement>
<proof>
<p><veil>Consider the partial sums <m>S_n = \sum_{k=0}^{n} r^k</m> and compute <m>(1-r)S_n</m>, then take <m>n\to\infty</m>.</veil></p>
</proof>
</theorem>

<p>The eigenvalues of <m>\begin{pmatrix}2 \amp 0\\0 \amp 3\end{pmatrix}</m> are <veil><m>2, 3</m></veil>.</p>

</section>

<!--
Various author tools are planned. Note the embedded "todo"
elements above. An XSL transform in-testing will extract them
Expand Down
141 changes: 141 additions & 0 deletions js/pretext_add_on.js
Original file line number Diff line number Diff line change
Expand Up @@ -1241,6 +1241,147 @@ document.addEventListener("click", (ev) => {
setTimeout(() => button.classList.toggle("copied"), 1000);
});

/**
* <veil> interaction
* ------------------
* Purpose: Provide show/hide behavior for <veil> in HTML:
* - When hidden: show a small button (.veil-toggle)
* - When revealed: show bordered content (.veil-content) that can be clicked to hide
*
* Notes:
* - Real <button> for the toggle, with aria-expanded kept in sync
* - Focus moves into revealed content; hiding returns focus to the toggle
* - Enter/Space work on both the toggle and the revealed content
* - Clicks on interactive descendants (<a>, <input>, etc.) do not hide
* - MathJax: On first reveal, request typesetting of the revealed node
*/
(function () {
'use strict';

// Prevent double-initialization if this script runs more than once
if (window.__PTX_VEIL_INIT__) return;
window.__PTX_VEIL_INIT__ = true;

// Centralized selectors
const SEL = {
container: '.veil',
toggle: '.veil .veil-toggle',
revealedContent: '.veil.revealed .veil-content',
pressed: '.veil.revealed .veil-content.is-pressed',
};

// Elements considered "interactive" inside .veil-content: don't toggle on them
const INTERACTIVE = 'a, button, input, textarea, select, label, summary, details';

function isInteractive(target) {
return !!target.closest(INTERACTIVE);
}

function parts(container) {
return {
toggle: container.querySelector('.veil-toggle'),
content: container.querySelector('.veil-content'),
};
}

function typesetMath(node) {
// MathJax v3
if (window.MathJax && typeof window.MathJax.typesetPromise === 'function') {
window.MathJax.typesetPromise([node]).catch(() => {});
}
// Fallback: MathJax v2
else if (window.MathJax && window.MathJax.Hub && typeof window.MathJax.Hub.Queue === 'function') {
try { window.MathJax.Hub.Queue(['Typeset', window.MathJax.Hub, node]); } catch (_) {}
}
}

function reveal(container) {
if (!container || container.classList.contains('revealed')) return;
const { toggle, content } = parts(container);
if (!content) return;

container.classList.add('revealed');
if (toggle) toggle.setAttribute('aria-expanded', 'true');
content.removeAttribute('hidden');

// Move focus into content for keyboard users
content.focus({ preventScroll: true });

// Typeset any newly-visible math
typesetMath(content);
}

function hide(container) {
if (!container || !container.classList.contains('revealed')) return;
const { toggle, content } = parts(container);

container.classList.remove('revealed');
if (toggle) toggle.setAttribute('aria-expanded', 'false');
if (content) content.setAttribute('hidden', 'hidden');

// Return focus to the toggle for predictable keyboard flow
if (toggle) toggle.focus({ preventScroll: true });

// Clear pressed visual if present
if (content) content.classList.remove('is-pressed');
}

// Click: reveal via chip, hide by clicking inside revealed content (except on interactive children)
document.addEventListener('click', (evt) => {
const btn = evt.target.closest(SEL.toggle);
if (btn) {
reveal(btn.closest(SEL.container));
return;
}

const content = evt.target.closest(SEL.revealedContent);
if (content) {
if (isInteractive(evt.target)) return; // allow links/inputs to work normally
hide(content.closest(SEL.container));
}
});

// Keyboard: Enter/Space on either the chip or the revealed content
document.addEventListener('keydown', (evt) => {
const key = evt.key;
if (key !== 'Enter' && key !== ' ') return;

const btn = evt.target.closest(SEL.toggle);
if (btn) {
evt.preventDefault();
reveal(btn.closest(SEL.container));
return;
}

const content = evt.target.closest(SEL.revealedContent);
if (content) {
evt.preventDefault();
hide(content.closest(SEL.container));
}
});

// Pressed visual: add on pointerdown; remove on any pointerup/cancel anywhere
document.addEventListener('pointerdown', (evt) => {
const content = evt.target.closest(SEL.revealedContent);
if (!content || isInteractive(evt.target)) return;
content.classList.add('is-pressed');
});

function clearPressed() {
document.querySelectorAll(SEL.pressed).forEach((el) => el.classList.remove('is-pressed'));
}
document.addEventListener('pointerup', clearPressed);
document.addEventListener('pointercancel', clearPressed);

// Initialize ARIA explicitly (useful if HTML was serialized without it)
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll(SEL.toggle).forEach((btn) => {
if (!btn.hasAttribute('aria-expanded')) btn.setAttribute('aria-expanded', 'false');
});
});
})();


document.addEventListener("DOMContentLoaded", () => {
const elements = document.querySelectorAll(".clipboardable");
for (el of elements) {
Expand Down
5 changes: 5 additions & 0 deletions xsl/pretext-common.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -1755,6 +1755,11 @@ Book (with parts), "section" at level 3
<xsl:message>PTX:ERROR: current conversion needs an implementation of the "code-wrapper" template</xsl:message>
</xsl:template>

<xsl:template match="veil">
<xsl:message>PTX:WARNING: &lt;veil&gt; encountered but this conversion has no implementation; content suppressed.</xsl:message>
<!-- no output by default in -common -->
<xsl:text></xsl:text>
</xsl:template>

<!-- The content of a "pre" element is wrapped many ways, -->
<!-- but the content itself is always strictly text -->
Expand Down
Loading