Scribble: How to wrap a subsection in a custom `div` element?

I'm trying to write a function that wraps a subsection, i.e. the subsection and the content belonging to the subsection, in a div element with a custom class.

The function without the div wrapping currently looks like this:

@(define (glossary-entry2 #:cross-reference? [cross-reference? #f]
                          #:stub? [stub? #f]
                          title-text
                          level
                          . text)
   (list
     (subsection #:style 'unnumbered title-text)
     (paragraph empty-style (elem (bold "Level: ") (symbol->string level)))
     text))

and is used like this:

@glossary-entry2["Arity" 'basic]{

The arity describes how many arguments a function can accept. Everything from
zero to infinitely many arguments is possible. Note that functions can have
optional arguments, so even for a specific function, the arity may not be a
single number.

Arity refers only to positional arguments, not keyword arguments.

See also:
@itemize[
  @item{@secref*["Procedure" 'glossary] @in-g}
  @item{@secref*["Keywords_and_Arity" 'reference] @in-rr}]
}

The workaround with elem, which I described here, doesn't work since elem can't contain arbitrary content, e.g. a part or itemization. (I get a corresponding contract violation.)

This thread may be related to my question, but it "doesn't work" because I couldn't find out where sxml->element is defined.


Context: I want this for the implementation of this ticket. Depending on the checked boxes, some JavaScript code could go over the divs for the glossary entries and make them visible or invisible.

I am wondering whether the "General Sibling Combinator" CSS selector could be used instead?

If the subsection is given a specific class, say, "entry" then .entry ~ div can be used to select siblings.

1 Like

Interesting, I didn't know about this selector. :slight_smile:

That said, as I understand the linked CSS documentation, this would usually select too much. For example, if I have

<h4 class="basic">Title 1</h4>

<p>foo</p>

<h4 class="intermediate">Title 2</h4>

<p>bar</p>

<h4 class="basic">Title 3</h4>

<p>baz</p>

h4.basic ~ p would select all sibling ps after the first h4.basic, which would include the h4.intermediate.

Actually, if the whole content of each subsection was wrapped in a div, the selector would work, since the documentation says "[...] and are children of the same parent element." But this again would require the div I asked for. :wink:

Meanwhile I tried two other approaches:

  • Wrapping the content in a nested-flow caused a contract violation because a nested flow can't contain a part (implied by the subsection).
  • The xexpr-property style property, as part of an elem, passed through the HTML I specified, but it put it inside the p tags from the element, so it generated invalid HTML like <p><div class="intermediate"></p>.

I can think of other workarounds that might work "somehow", but I have no idea how to apply them (again, if it's even possible):

  • Capture the generated HTML string and post-process it before it's written. For example, the Scribble document could include some distinct text like [div intermediate], which would later be replaced by <div class="intermediate">. However, I have no idea how to capture and post-process the HTML as part of raco setup.
  • After the HTML file has been generated, post-process it with some custom code. However, I have no idea how to plug this into raco setup.

Maybe someone else has advice along these lines?

@mflatt @samth :

What I'd actually like to have in Scribble:

  • A way to wrap some content inside given HTML tags (specified as a style). Essentially, this would be like elem or nested-flow, but with the constraint of forbidding parts and other contents removed; and
  • A way to insert literal HTML in a document, similar to xexpr-property, but without insisting of enclosing tags (like p in the above description). This approach could be generalized for multiple formats/renderers, e.g. LaTeX or Markdown.

For my specific use case, either of these approaches would work, but I think both would be useful. If I had decide on one, it would be the second since it's more general. However, depending on how the Scribble document is written, this might create invalid HTML because it may not be clear which particular HTML code Scribble generates. So the first approach would probably be safer if it could be used.

I don't think you've overlooked any way to do this at the Scribble level, right now.

If we were to add something, I imagine it would take the form of a style property for a part. (The subsection and similar functions accept style properties that get propagated to a part). The HTML renderer would look for the style property in render-part-content and add a div layer around the list that it currently returns.

One caveat is that a part may have subparts that are rendered on separate HTML pages. I imagine that this div layer would wrap only things that are rendered on the same page as the part title. There's plenty of precedent for a style property that behaves specific to HTML pages, though.

1 Like

As I understand this, the style properties of a (e.g.) subsection would be added to the div? I think it would be good to be able to specify separate style properties for the div and the h* tags (for example, for specifying a class for the div and removing the top border for the h*).

That said, if I had to choose one approach (adding the style properties to div vs. the h*), I'd prefer the style properties to be added to the div because then I'd still be able to style the h*s with an additional CSS file with

div.my_class h4 { ... }

while still being able to specify style properties for the div as a whole.