#Styling

1477 messages · Page 2 of 2 (latest)

edgy harness
verbal dove
#

Oh, it's already works like that...

umbral sandal
#

All red

edgy harness
verbal dove
umbral sandal
edgy harness
umbral sandal
#

not all text red

edgy harness
verbal dove
#

Oh yeah, true

edgy harness
#

both are just styles

umbral sandal
edgy harness
#

maybe your session expired?

umbral sandal
#

private tab, oh... maybe that's why?
some other websites refuse to work when opening in private tab in Firefox.

edgy harness
#

you need to use the share link with r and not the project link with p then

#

if you are logged in you are redirected to the p link

verbal dove
#

Though feels hacky

edgy harness
#

yeah does feel hacky

#

and if someone makes their headings red, we are back to square one

umbral sandal
edgy harness
umbral sandal
#

classic ;)

edgy harness
#

I sent the p link :D

#

It's edited now

edgy harness
#

whereas logged in users that have entered the project through a share link at least once become "external project members". they can access the project through the p link, see it on their dashboard and can continue to open it even if the share link is revoked. (unless the owner kicks them through the share menu)

umbral sandal
prime quail
#

This one is tricky because I know what the issue is

#

Is your question what it should be? Or what I think it is now?

edgy harness
#

What it should be

#

Not testing anybody ^^

#

Just trying figure out what to do here

umbral sandal
edgy harness
prime quail
#

Black for all of them

#

Funny enough

umbral sandal
bleak meadow
#

What about 1,2,8 red and rest black, so that only the style that is in place when the page is created is used (for the first page only styles before any content)

edgy harness
wraith cypress
#

Show footnote set text

#

Or something

prime quail
#

I think the styling should not be applied because conceptually, we're not placing the content where the styles are active, the footer should be used to configure the styling, if the footnotes land int he margin as margin notes, then the margin need as way to configure figure the style

#

I think margin and footer are different areas when it comes ot styles than the usual global document scope which go into the implicit pages

#

The marker however

#

Should be Red

#

Since it's on the page where we have those styles active

edgy harness
#

yeah sure

edgy harness
prime quail
#

I think we'd need something like show page.footer: set text(red)

#

Or more specifically show footnote.entry: set text(red)

edgy harness
prime quail
#

Good point

umbral sandal
# edgy harness I guess with the layout I can see your point. But with the heading boldness it's...

No, exactly the same thing. You just wanna cite something, add some context information to the heading by adding a footnote to it. So why should the implicit heading styling be applied to the footnote that just exists to provide some info/context? But since you're again setting the #show heading: set text(red) explicitly, you are signing up for the footnote to be also red. At least potentially. My first thought was that it will apply. Now it's a bit more mixed.

prime quail
#

I'm not sure

umbral sandal
prime quail
#

I think it all comes down to footers and margins being special here

edgy harness
prime quail
#

Nothing ever goes into the footer or margin unless we shove it there explicitly

edgy harness
prime quail
#

Yeah that makes sense

#

Tricky

bleak meadow
# edgy harness how would the compiler distinguish 7 and 8?

I don't know how the compiler works in this regard, but there would have to be some kind of "content start boundary" before the heading, so the heading show rule doesn't affect the top-level style. The show-all rule in 8 isn't bound to a specific element and should affect the top-level styles. I can imagine that this doesn't align with how the styling works now though

edgy harness
#

Your proposal is actually very close to my thinking, but defining "content start boundary" well is tricky

#

And then there is also the problem that not all "prefix styles" remain active throughout the full document. Consider e.g.

#set text(font: "Helvetica")
#{
  set text(red)
  [I am the first content]
}
More here though. #footnote[Hi]

The first content boundary is right before the word "I", but the red text should not affect the footnote because it doesn't remain in effect until the end of the page.

#

Given a suitable definition of "content boundary", what might work is taking some sort of intersection of the styles at the first content start boundary and at the last content end boundary.

#

What happens currently, is that we take the intersection of the styles of all elements on the page. The difference here would be that we don't take the styles of "I" itself, but somehow the styles right before it.

#

What might also be possible is to continue taking the intersection of all elements and somehow cut off part of these styles that come from constructor calls (like text(red)[..]) and show rules. But then 8 will again not be red.

bleak meadow
edgy harness
rancid fog
#

My 2ct: Red for all but 8.3 or for none of them would feel sensible, I think.
To circumvent the #show heading: set text(size: 50pt, weight: "bold") and figure problems, footnotes might get the properties of “generic text on the page scope” (whatever that means), but scaled down.

bleak meadow
edgy harness
edgy harness
rancid fog
#

Ehh, maybe everything that was applied to text using a set-rule before the first main-body content of the current page?

bleak meadow
edgy harness
#

Other than that, maybe the handling of a template as a type/element is itself already suboptimal as that would kind of imply that a template element is placed in the document, whereas it should rather surround the document to be able to apply styles to it
Maybe, though I think it could be a nice pattern, since it allows set rules on the template parameters and get rules in extra template elements to retrieve them.

#

Funnily enough, I think \begin{document} would sort of fix our problems since it establishes a clear content boundary ^^

umbral sandal
#

Only now did I realized why the sudden quiz.

wraith cypress
#

Could an element explicitly define which styles it inherits (i.e., inherit text font but not colour)

solar forge
#

I think we need to consider where a style's scope ends. With show: ... the scope of the rule goes conceptually beyond the end of the page—beyond the end of the code really; it's very meta. show heading: ... however, very explicitly is scoped to the heading. And if you were to add more content after the heading, it would not be styled as the heading

#

The 'if you were to add more content' point really drives this home for me. If, instead of writing #footnote[], I explicitly just appended at 'the end of the page' (where is that? 🤔); what styles would apply then? I think using those styles in the footnote entry could prove to be intuitive and predictable

#

One issue is what happens when a block with different styles is broken accross pages

#

Arguably its styles should not be applied to the footnote entry, but that isn't so true for some elements (like #chapter[...] and such)

#

I think an important distinction for an element is whether it should come 'above' pages or within them

#

Maybe to define content that should encapsulate pages and whose styles should be inherited within, an explicit call to page (or a new pages? 🧑‍🍳) must be made:

#let template(
   ..whatever,
   content
) = {
  set global_rule(value: "something") // This is applied to footnote entries, headers and footers (even if defined outside of this call to `template`)
  page(..arguments_if_you_want)[
    = Look at my template!
    === It's so cool!
    #show raw: set text(red) // This is not applied to footnotes, headers, or footers.
    #body
  ]
}
edgy harness
#

So it is just like show heading in that respect

edgy harness
solar forge
#

Well if I have a block with set text(red) and it spans across a page boundary, should the footer/header/footnote entries on that boundary be styled as the block? oftentimes not, as with #proof[...] or #note[...], but sometimes it is so, as with #template[(everything)] or #chapter[...]

edgy harness
#

Do you mean block in the sense of {..}/[..] or #block[..]?

solar forge
#

Mostly block

#

of course there will be semantically similar elements that aren't block-level in practice

edgy harness
#

I think styles that are local to a block shouldn't apply to footnotes

solar forge
#

usually

edgy harness
#

It becomes a mess where the footnotes pick up random local styles

solar forge
#

for sure

#

perhaps I'm grouping into blocks some elements that aren't actually

#

(template, chapter, ..)

edgy harness
#

yeah, they aren't really

#

since I asked yesterday I've come up with a scheme for the page styles that actually works really well. I'm still figuring out some edge case details, but it behaves pretty intuitively (at least to me). it's a tad complex to explain, but let me try:

solar forge
#

because pages aren't (usually) explicitly typed in the markup, it becomes necessary to otherwise define the hierarchy of document elements in relation to pages; whether they encompass pages (such as chapters)—and thus their styles are inherited by the special page elements, or they come within pages (even when broken into multiple parts), such as equations, lists (usually), code blocks, etc. that then keep their styles only within their content

edgy harness
#

How it works: As currently, we collect the styles that are shared by all elements on the page. As a fallback if there are no elements, we use the styles active at the pagebreak that introduced the page (or at the very start, we use the default styles). Then, to produce our page styles, we filter this list of styles according to a few rules:

  • Globally active styles (default or from World impl) are always kept
  • Other styles are only kept if they are outside && (initial || liftable)
  • "Outside" means they were not produced within a show rule. (Still tweaking whether I reenable outside-ness when someone uses set page within a show rule to "break free". However just pagebreak definitely won't break free because of show heading: it => pagebreak() + it. There could also be some explicit function call to break free like proposed above.)
  • "Initial" means they were active at the pagebreak that introduced the page. Since these are intuitively already active, they should be kept in any case. (E.g. text(red, page(..)) makes the footer red.).
  • "Liftable" means they can be lifted to the page-level even though they weren't yet active at the very beginning. Set rule styles are liftable as opposed to direct constructor calls. (E.g. for set page(..); set text(red) the red text is kept even though it comes after the weak pagebreak from set page whereas for set page(..); text(red)[..] the header isn't red.

This all sounds complex, but I think it won't feel complex to the user. It's also actually not particularly complex to implement.

solar forge
edgy harness
# solar forge one way to do this is by using the existing `page()` function to declare that th...

This is sort of the idea of "initial". Originally I had the condition initial || (outside && liftable). That works almost always as well, but had the small remaining problem that in:

#set page(numbering: "1")
#show heading: it => pagebreak(weak: true) + it
= Introduction
// No more content!

The numbering was still heading-styled because the pagebreak and all of the page content was contained in the heading.

#

Due to this I changed the condition to require outside always. This means an element show rule can't configure pages anymore. To support this, one could either require an explicit opt-in like you propose or use the way where set page implicitly breaks free, but pagebreak doesn't.

solar forge
solar forge
# edgy harness This is sort of the idea of "initial". Originally I had the condition `initial |...

I think this would've also broken long code blocks or equations, so your change was definitely the way to go. I'm still a little concerned about liftable though; what if I do #set line(stroke: red), just because it happens to be convenient for some figure, and then I don't have a reason to set it back yet, because I'm not using any lines. Initially this seems fine, then I finish the current page and suddenly the next page's header breaks because it has a decorative line in it

#

I think simply 'is this a set rule?' is not enough to determine whether the rule should be liftable. If I'm using the codelst package and it happens to use a set rule in its implementation, everything is all fine and dandy as long as my code blocks span less than one page, but once they break twice, suddenly that rule applies to the middle page entirely

#

Sometimes you just need to switch text language or direction in the middle of a page, but you (probably¹..?) wouldn't want your header to change unless you explicitly gave it a new value.

¹ - it's definitely possible to write your header such that it looks good in all of your documents' main langauges, but it's still not obvious whether you want to impact the headers and footers after switching languages. I think the principle of least surprise leads me to prefer opt-in here (with show page.header: set text(lang: ...)?)

edgy harness
#

I think I need to clarify something: It is not about the shared styles of every element on one page. But rather by every element on one page run.

#

And a page run is everything between two pagebreaks or set page thingies.

#

So pagebreaks that occur naturally during layout have no effect at all on this.

edgy harness
solar forge
edgy harness
#

Yeah, I should've been clearer about that. A few people's comment now make more sense to me.

serene hound
#

I’m not sure if this has been discussed already, but are there plans to make it possible to manipulate content after styling (show rules) have been applied?

The need for this kind of thing comes up in e.g. https://github.com/Jollywatt/typst-wordometer/issues/2, where we need to manually walk the content tree.

GitHub

A small Typst package for quick and easy in-document word counts. - Issues · Jollywatt/typst-wordometer

edgy harness
serene hound
#

Yeah. I sense it would be a bad idea to open that Pandora’s box.

#

That said, I still haven’t figured out a good way of implementing a word count. Typst might need something like that built in!

#

E.g. adding a contextual function that provides access to the plain text contained in an element, as a string without any styling, would make a wordcount possible.

edgy harness
#

Yes, I believe it needs some support from the core, whatever concrete thing that may be

edgy harness
#

@drowsy apex if you have ideas for the page stuff, I'm all ears but it's quite tricky

#

but I didn't really want to keep it open as a bug because I think no matter how you define it, there will be some ambiguous cases (though I'd be happy to be proven wrong)

drowsy apex
#

i mean, the current behavior isn't that bad, in the sense that at least the most egregious bug cases are gone, it's just a bit unexpected

#

at least the way i described it is how i think a user would see it

#

I agree it isn't a simple problem though

#

especially from a technical PoV

edgy harness
#

the thing is, the styles don't really go beyond the content block

#

rather, the page shrinks to just the content in the content block, conceptually

drowsy apex
#

maybe we are just missing some extra sugar in the #show: syntax 😛

edgy harness
#

I'm not sure making show: magic is great either

#

Right now it's very well defined

#

Also, I assume you'd want both code and content blocks to behave the same right?

#

And making code blocks block the page styles might have some rather unfortunate consequences

#

For example, the font is often configured in a code block

#

I guess it all depends on how you would change show:

#

anyway, I do low key hate how I solved it in the end (essentially with a bunch of special cases and ifs), so don't get me wrong, I'm open to discussing it

drowsy apex
edgy harness
#

Let's assume Typst had page numbering enabled by default

#

I mean this:

#let template(body) = {
  set text(font: "My Font")
  body
}

#show: template
drowsy apex
#

ah yeah, im not considering a distinction between code and content block here

#

thats why i added "ignoring how template functions are defined at the moment" to my answer

#

the distinction i was envisioning here was between using top-level styles (#show: ..., #set outside of any #[ scopes ]) and non-top-level styles (inside a #[ scope ])

#

so #show: would sort of "promote" the template's styles to work just like top-level #set

#

since the #show: itself is top-level

#

here "top-level" purely from a syntactic / user PoV

edgy harness
#

so the {..} in template scopes it, but show: promotes it?

drowsy apex
#

yeah

#

basically

edgy harness
#

what if I add more indirection?

drowsy apex
#

another show: in the template, you mean?

edgy harness
#
#let hi(body) = {
  set text(font: "My Font")
  body
}

#let template(body) = { hi(body) }
#show: template
drowsy apex
#

hmmm

edgy harness
#

would be rather surprising if that behaved differently

drowsy apex
#

i think we'd recursively apply the top-level semantics

#

so you could show: hi

#

but not hi(body)

edgy harness
drowsy apex
#

yes

edgy harness
#

that would break my expectations a lot

drowsy apex
#

that's fair

#

to be fair that isnt strictly required, it's possible to only change the first half of the equation

#

but i fear that could be inconsistent or limiting

#

or maybe not

#

maybe just promoting templates is enough and im overthinking it

edgy harness
#

I gotta go, my sister wants to do something

drowsy apex
#

yea sure

#

you're not supposed to be here anyway

#

😂

#

gotta have your fair share of rest as well 😎

bright karma
# edgy harness ``` #let hi(body) = { set text(font: "My Font") body } #let template(body) ...

Could we model this as there being "hidden" header and footer elements that are placed before/after the top-level content?
Ex:

#set page(numbering: "1")
#body

Would behave like:

#set page(numbering: "1")
#hidden-header-element
#body
#hidden-footer-element

With a rule that hidden-header-element places itself as close before the first content on each page as possible, so it comes after any pre-content show rules on any page, and hidden-footer-element does the inverse?

Then the difference would be:

#func[ #body ]
---
#show: func
#body

Respectively becoming:

#hidden-header-element
#func[ #body ]
#hidden-footer-element
---
#func[
  #hidden-header-element
  #body
  #hidden-footer-element
]

Which I think matches our intuition?

edgy harness
#

The function call is completely gone by the time Typst resolves styling

bright karma
#

I think I'm not envisioning show: template as "promoting" the template to the top-level, but instead that the hidden elements are treated like real content and inserted before realization starts at all. Then the function call retains its scope because the structure remains nested, e.g. {document: [head, func(body), foot]}, vs. func({document: [head, body, foot])})

gaunt pagoda
#

Maybe it's a fundamental problem that header and footer are (apparently?) thought/meant/implemented to be on the same level as the content of the actual document? For me, header and footer are information about a page, you shouldn't even have access to them from within the document.

#

Rather, it should maybe be thought that there is a document call that wraps all content and is responsible for layouting. And if you want to apply set rules to header/footer you can't do that from within the main content, you have do it before the document call.

(Or from "within" the call. Would be cool if you could change the document function to make your own layout or so.)

prime quail
#

I think the biggest problem is not headers and footers, but top-level content vs explicit pages

#

it shows in the same way, styles get complicated because of this

gaunt pagoda
#

Here is a concrete proposal.

If you only write text, there will be no header or footer. There are no issues as to where set rules should apply to.

Hello World

If you want header and footer, you have to wrap everything in something I call header-footer-layout for now. If you put set rules before that, they DO apply to header and footer.

set text(50pt)

show: header-footer-layout.with(header: ...)

Hello world

If you put the set rule after (i.e. as argument to the function call), they are scoped as usual and don't apply to header and footer.

show: header-footer-layout.with(...)

set text(50pt)

Hello world
#

What do you mean with explicit pages? Calls to page? Can you give an example for the issue? @prime quail

prime quail
#

your example kind of illustrates what i mean in a differnt way

#

consider the show rule to simply be a call to page yes

gaunt pagoda
#

with set rules always being scoped plus my model above i don't see any issues

prime quail
#

the problem is that this doesn't always match intuition, there are a few times laurenz has posted stuff in here where people weren't really in agreement what certain rules should do in edge cases

gaunt pagoda
#

hmm, can you provide an example? what i've seen so for is that people want to break out of their scope ("edit their parent/sibling") and i think it just shouldn't be allowed. there should be other ways/syntax (such as my proposal above) to do the things that people want to do when they want to break out of scopes.

prime quail
#

discord doesn't let me search only in this thread so i actually can't provide an example at this moment

prime quail
#

this doesn't even talk about footers, but things that end up in footnotes

#

and there are plenty of other examples somewhere in here

gaunt pagoda
#

Ok so I guess you're talking about how footnotes should be styled? Like in the style of where they are referenced or differently. I think differently. I don't think of the footnotes as being a sibling of the main content, they are ABOVE it. So I think, just as with the header/footer, you need to have a special "footnote provider" function. Styles applied before that provider call affect footnotes. Styles inside the main content don't affect footnotes.

#

This has also other advantages. For example, sometimes people want footnotes within some environment (like a theorem box) to appear at the end of the box, not at the end of the page. You can easily do that by wrapping your theorem content in a footnote provider, and then wrapping that in your block or whatever.

Or when you have columns, and you want to be able to decide whether footnotes are displayed per column or per page. It just becomes a matter of whether you wrap columns in a footnote-provider or the other way around.

drowsy apex
#

Sounds like you might be on the way to my proposal though, by suggesting 'show:' is treated differently

edgy harness
gaunt pagoda
#

Yeah I agree, but maybe one can provide some default fallback or so. I'm just trying to preserve thr semantics because I think throwing away the hierarchy will bite you sooner or later.

gaunt pagoda
edgy harness
#

The problem already manifests itself without any show: though. Just a simple set text(font: ..) followed by a footnote vs text(font: ..)[all the content] raises the question of what the footnote font is in both cases.

gaunt pagoda
#

If we want to set things up in the hierarchy from below in the hierarchy with clear semantics maybe clear selectors are a solution, like I guess discussed before:

set footnote.text(...)
set footnote > text(...)
set header > text(...)
edgy harness
#

Those would be okay for specifically styling footnotes. But what I want is more that footnotes are affected by all your global styles, like set text(font: ..), by default, without you having to think about it. Having to set the footnote font separately is not DRY and easy to miss.

gaunt pagoda
#

I guess my take is that:

  • it doesn't matter where the footnote call is (think of it as throw), but the where the "footnote provider" (even if not explicit function/type) is (think of it as the catch)
  • the footnote "provider" lives in the global scope, hence footnote style can only be edited in the global scope. so e.g. set text(...) at the very beginning of the source file will affect footnotes.
  • but set text(...) in any deeper scope won't affect footnotes
  • show: my-template doesn't open a scope, but keeps the current (usually global) scope (at least when it comes to set rules)
  • if you STILL want to access the footnote provider from within the deep hierarchy, use some sort of new selector which provides access to the global scope
gaunt pagoda
#

essentially a page selector could become the selector that provides access to the global scope

gaunt pagoda
#

or just set > text(...)!
(similar to how it's done for paths. writing some/little/path is relative to where you're currently, but starting with / like /some/little/path is from the root)

west coyote
#

show rules appear to have some slightly odd behavior in determining fixed points. In particular, I have a show rule where I have

show [].func(): seq => {
    chld = seq.children
    // code which conditionally modifies children
    if [].func()(chld) == seq {
        seq
    } else {
        [].func()(chld)
    }
}
```and this works fine.
However, if I replace that last if else statement with just `[].func()(chld)` it recurses infinitely. From this I am guessing that show rules look for fixpoints via some sort of referential/pointer comparison for "is literally the original object" rather than based on equality and I'm wondering why? 

It seems odd and counterintuitive, given that referential equality seems otherwise entirely obfuscated (at least afaik) within the language

Is it purely a matter of performance? I would imagine non-equality should be obvious very fast in most cases and equality could check for referential equality first to make the most common case fast so that show rules could fixed point off structural equality with minimal impact 

(You could also do even less performance impact when this is unused by only checking referential equality and if a given rule hits max depth, try again with structural equality, and if that succeeds, tag that as a "structural show rule" for future applications so that the penalty is only incurred at all for rules that use it, though this would have the disadvantage of potentially creating counterintuitive behavior in pathological cases (e.g., if you have structural equality after some number of iterations and then referential equality after *more* iterations, then if structural equality is supported you would expect it to stop there but it would actually continue until referential equality) and also of incurring a performance penalty when structural equality is used.)
edgy harness
west coyote
#

Why not an equality check? It seems reasonable to stop application once application doesn't change the value?

edgy harness
# west coyote Why not an equality check? It seems reasonable to stop application once applicat...

I'm not quite sure what you are proposing exactly. What would be checked against what? Show rule application doesn't happen in a loop, but rather lazily on-demand (if you write show heading: it => block(it) that rule runs first and then block layout runs and then during block layout the default show runs). So we somehow need to transfer the information that it shouldn't be shown again with this rule through the whole thing.

west coyote
#

Hmm, I'm not sure I fully understand how show rules work, but I'm talking about cases where the show rule output produces something which matches the show rule, as in my original post for example

edgy harness
flat socket
#

So, i had the thought of working on ancestry/nested selectors.
design wise (api design i guess) i'd want something like .contains, the exact word is probably trivial to change

dedicated syntax (like a > b or whatever) doesn't fit in with .after/.before

edgy harness
#

I agree

#

What I think is difficult from a design PoV is that you'd often also like to bind the parent to a variable

#

(in a show rule)

flat socket
#

hm
(i'll experiment more and look at stuff after the exam-thing i'm currently sitting in)

prime quail
#

I wonder if ancestry would also allow us to get neighboring content in shoe rules

flat socket
#

i think that'd have to be something seperated

#

like, i'd very much like to do that too, but i think that's too "different"

#

to be in the same pr

flat socket
#

okayyy
would .contains only check direct children or recursively? or would that be a flag or two different functions?

edgy harness
#

I think it should probably be recursive, though both might be interesting

#

but non-recursive would be a lot harder to pin down the exact semantics I think

prime quail
#

The hardest thing with more specific selector mechanisms is definitely the semantics

#

There's also the issue of the structure and representation of content not really being stable, so expressing a thing in one version may just not have any use in another because the structure changed and does no longer match the selector

edgy harness
# prime quail There's also the issue of the structure and representation of content not really...

I was just fixing a small 0.13.0 bug and was reminded of the discussion about auto-resolving of contextual properties. In particular, I changed how text.hyphenate is resolved internally (it does not use #[resolve] anymore because it needs to resolve relative to the justification settings of a particular paragraph rather than generic par.justify). This is sort of similar with a tension between things being unstable internally and the pain of resolving all the auto values manually in Typst code.

flat socket
#

i feel like a lot would be easier if there was a stable structure, but i guess that discussion can be deferred until there is

#

anyway, i'll try to build a shitty test impl the next few days :3

flat socket
#

i got it working!!!

(source code for that:

For testing, lets search for equations in headings

$a$

= Some Equation: $b$

$c$

#context query(selector(heading).contains(math.equation)).first()

)

#

tho one thing i ran into wihle testing is that if you don't pay attention: .contains is also valid on a query, and means something different there

maybe the selector should be called something different, just so people don't get confused why their .contains returns a boolean?

#

i'll go write some documentation and stuff and will pr it later today then :3
(preferrably, before i go to sleep so there might be some reviews on it when i wake up)

prime quail
#

Cool, how's the performance looking? I know that's a big concern for Laurenz when it comes to introspection

flat socket
#

haven't tested it and am probably also gonna do the tests later because i am getting tired

#

idk if it'd be smarter to wait with the PR, but also i'd prefer opening it now so i can get feedback while asleep

prime quail
#

For the naming, you could reverse the function by doing within and swapping the arguments of selector and that

#

You can open a draft PR

#

Doesn't have to pass yet

flat socket
#

will do!

flat socket
#

but yes .within makes sense :3

#

i do see a contains selector also being useful, but that's easier to hack around when this is implemented

prime quail
#

My concern is that adding contain later like when you're building a selector programmatically completely reverses which primary element you're looking for

flat socket
#

no, meant that can probably be done reasonably easily using within from inside a document

prime quail
#

I'm not quite sure what that means or what that is answering

flat socket
#

if you actually need to get all items containing another matching a selector, then that's probably relatively easy using within either way

prime quail
#

Ok now I get it, I thought that the contains selector what select the math element

#

Wait it does

#

Ok what I mean is that the meaning of the argument of selector depends on the function later called on the returned object:

// selects foo
#selector(foo)
#selector(foo).after(<bar>)

// selects bar
#selector(foo).contains(bar)
#

If these calls are at different points in code then this becomes hard to reason about which is why I suggested the within variant

flat socket
#

yes, within makes moe sense

#

that's also how it's called in the roadmap

#

i'm just not gonna rename it now because i'm getting tired and wanna go to sleep

#

i'll do that tomorrow

prime quail
#

Gn :)

flat socket
#

have to first go home, but thanks! :3

flat socket
#

okay, i changed it into within

how do i generate sample output for tests? (also, is there anything specific i should write a test for/take into account?)

prime quail
#

checkout te contribution markdown document

#

it has a section about tests

#

running with UPDATE=true will generate new references for tests

flat socket
#

ahhh

#

thanks

#

i guess i then overread that ^^

flat socket
#

with the current implementation, duplicate results can happen if there's nested matching ancestors.
would deduplication in this case be desired?

prime quail
#

can you show an example?

flat socket
#
#figure[A #figure[#strong[a] #label("strong")]]

#context query(selector(<strong>).within(figure))

leads to this

#

that's correct since well, the ancestor is matched twice
but also i could see this be really annoying

drowsy apex
#

It doesn't seem to be correct, you should verify that a show rule on that selector would only run once

#

For example, #show selector(<strong>).within(figure): it => [#it;a] should display only "a" instead of "aa"

#

p.s. I'm completely out of the loop, just responding to this message

flat socket
#

https://github.com/typst/typst/pull/5867/files#diff-d34b5a495c4bb9fb0e5383cc47f4ed511bfb5e7beb43871c753a2077bca46c7dR453

unless i'm misunderstanding it can't be used as a show rule

did that out of caution because honestly i'm not sure about the implications of that, since before and after aren't showable either

GitHub

This implements a selector that gets all items within another item. Closes #1926 (and also is the second feature on the roadmap under "Styling")
(originally was discussed as .cont...

drowsy apex
#

well, that's ok, but im thinking more in terms of general semantics

#

if there were such a show rule it would end up matching twice

#

i think that should probs not happen

flat socket
#

agreed

edgy harness
#

@flat socket Some thoughts:

  • The Content::query function shouldn't be used. It's more of a hack that was introduced at some point for figures and I made the concession to have it there. But it's significantly nerfed compared to the proper Introspector::query as it will query in the raw content and thus can't see through show rules, context, or anything like that. For within to work as expected, it would need to make use of the Start and End tags to reason about nesting.

  • Compared to before and after, the within selector is also one that's much easier to support in show rules, so that would be interesting as well. That's where the bindings thing and the design complexity comes into play (i.e. you might want to write a show rule for a caption in a figure and have as bound variables both the figure and the caption).

flat socket
#

about the first: kinda was confused why it existed since i couldn't find any usages ^^'
i'll work on redoing that then, before that i don't think it'd be sensible to even approach the second point?

#

(approach the second point: working on it with code, sorry for bad wording)

edgy harness
#

we should probably add some discouraging docs on Content::query

#

however, on a note of caution: the second point might influence the whole design space, so deferring it could lead to wasted work

#

in general, this is a very intricate part of the compiler. I appreciate you giving this a shot, but I want to make clear upfront that the road to a merge might be bumpy

flat socket
flat socket
flat socket
#

(sorry for not doing much work recently, i'm on holiday and also when i do work on it said "work" is currently just... reading code and trying to understand what approach to currently even take ^^')

edgy harness
flat socket
#

:3

merry patrol
#

Does it make sense to keep context in the future world where custom elements are possible and their show rule already provides context (i.e., context expressions can be replaced with custom elements)?

edgy harness
prime quail
#

TLDR: they wrapped hydra in heading to get the same styling, but this inevitably caused it to create a heading in the location it starts searching from, which trips up its redundancy checks

#

I'm surprised that this doesn't cause a layout divergence

edgy harness
prime quail
#

The user wanted to apply heading.level:3 styling to their header, but this requires knowledge about thew default show rules and requires the user to update the those whenever typst changes its default styles

#

ideally the user could somehow do set styles(heading.where(level: 3)) (imaginary syntax) in the header to achieve the same thing

#

this is moreso about the defaults given by show: set

#

like the text size and weight

#

SO far the user would have to go inot the rust source to understand which text size and weight they'd have to apply, among other things

umbral sandal
prime quail
#

i suppose, but also without having to know which styles to get and re-apply

umbral sandal
#

#set text(context heading > text.size)

prime quail
#

the more i think about it I'm just asking for styles as values + the above issue + getting more than one style in total/providing default styles like those in the stdlib

umbral sandal
#

I thought you mean you just wanna a dictionary with default values

#

#default-styles.heading.text.size

prime quail
#

well this would probably suffice but it doesn't seem like the easiest thing to use for a novice

umbral sandal
#

I mean, it would definitely be nice to have. But it is also very close to revoke rules, i.e., revoke all. Then you can access the default values. But only for set rules. And then you only need to resolve the above feature to also get styles in show-set rules.

prime quail
#

while close it's distinctly different, since it's about getting and re applying rules instead of revoking them

#

but for now we'd first need styles as values in order to actually retrieve styles in a manner in which they can just be re-applied without any extra work

#

and then we'd either need to be able to collect styles from just s elector like heading.where(level: 3) or expose the defaults in the stdlib like heading.default-styles() where it returns styles as values in both cases

umbral sandal
#

we already can have styles as values, but not everything

prime quail
#

what

umbral sandal
#

and only in the context

prime quail
umbral sandal
#

Yeah, I remember Laurenz said that it's just not possible yet

umbral sandal
#

context text.size is a style and it's a value

prime quail
#

scroll up

#

or search for apply in here

edgy harness
umbral sandal
#

I also would like to have that.

edgy harness
#

Logically, it's sound I think, but there are certainly implementation challenges

#

So, it's still possible that some dealbreaker problem arises

edgy harness
#

whereas heading.where(level: 3) would be a selector

#

So we would need to match selectors against selectors instead of selectors against elements

#

Which may be possible, but is confusing, so I'm not sure how it would work exactly

prime quail
#

that's mostly because of the current implementation though is it? in theory since styles can be applied with selectors they should also be retrievable with them right?

edgy harness
#

perhaps yes

boreal notch
#

As there is no thread dedicated to templates, I think this the correct one for this matter.

A few weeks ago I posted this reply in the forum https://forum.typst.app/t/overriding-template-parameters-missing-social-convention-or-typst-design-flaw/2792/11 which I initially wanted to post here.

There I present a different approach for template packages that suggests a separation of styling and content in order to maintain adaptability of templates. I don't want to repeat the entire post here but for context, the main idea is that a template exposes two functions:

  • One for setting up a new style default with set and show rules.
  • And a second one that is responsible for generating content. It should not use such rules directly.
    This is instead of just a single function which is what (to my knowledge) all current template packages do.

A user will employ the template in the following way:

#import "template.typ"

#show: template.style   // ← we agree on some good canonical names here
// here is a good place to tweak template defaults, even concerning the page setup
#show: template.content

This approach will now even become part of the rewritten package docs (see https://github.com/typst/packages/pull/2164). There is of course the increased burden on the user who has to write two show rules instead of one but I think the gained advantages are worth it in many cases.

Oh, there is actually another approach I can see for the future that I will discuss below.

Typst Forum

Hi everyone (thanks for pinging me @laurmaedje, I was too busy to make a post in the last two weeks)! I recently put a lot of thought into template design and think that the issue is three-fold: a) How can the user of a template tweak styles that are set or shown by the template? b) How can the user manipulate the content (like a title page) ...

#

I also plan to write a practical guide (when I find time) on how to go about with the style-content-separated scheme because there are some nice things one can do with it. Moreover, I've been working on a template for preprints which explores several methods to give natural and full control to the user.

I believe that it is important to do some proper research on how to best write templates since this knowledge will shape the landscape of how Typst documents are created and used. A big thing with LaTeX for example is the stiffness of document classes that are hard to tweak by (up to semi-experienced) users. Typst could bring a better user experience when working with templates − also in this regard!

Also this is something that I feel needs to be done rather soon than late to prevent bad habits becoming the norm.

All of this should be a group effort. I could for example publish my template as a research-project for testing (it still has open issues) with typst-community and then everyone can contribute to it and discuss options.

prime quail
#

Interesting, I've actually went to great lengths to make my template configurable and I have a similar approach

#

All my styles (where manageable) are confined to their own template functions which are applied in the top level template

boreal notch
#

Interesting! Do you have a good example to check out?

prime quail
#

One sec

#

Some styles are applied atthe top level in there, others only within their components directly

#

No way to hook into those yet with default args

#

Not sure if I want to go that far since its mostly meant as an escape hatch, at which point is it still the same template if a user can change anything about it

boreal notch
#

This is actually very similar. Not with show rules but with functions but it's totally the same idea, I'd say from first glance.

#

Now, the second possible solution works like this:

A template (hypothetically) defines a type, say project, which is used instead of a function to wrap the content of the document. We use it like this:

#import "template.typ": project

#show: project // or project.with(..)

The default show rule for this type works like the second function from above (template.content). It contains no rules itself but generates content and wraps the document.

Now we need a second ingredient: default show rules for user-defined types. Just like heading brings a default style (bold, a bit larger etc.) out-of-the-box, user-defined types could be able to do the same.

All the rules from the first function from above (template.style) are applied through this default show rule and thus they can be overriden by the user

#import "template.typ": project

#show project: set page(margin: 1cm)

#show: project 

This is just with one (mandatory) show rule! This thought is still rather new and not yet thought out but seems promising.

#

Note that − in comparison to the other approach − one needs to write all overriding configuration in a show project: rule instead of just into the document. So in the end there's a downside to everything.

prime quail
#

Yeah I mean that's essentially how it should be at some point

boreal notch
#

That's the question

drowsy apex
#

fwiw we can probably experiment with this a bit using elembic

#

it has a default template thing like that for elements. where you can use show-set to override those set rules

#

will be nice once i can finish implementing custom show-set for completeness

#

but in general i think the main benefit of custom types wouldnt be that

#

but rather being able to style specific parts of the template, say, abstract, title page, stuff like that

#

without having to edit the source code

prime quail
#

Yes

#

I think so too

#

It's all just special selectors for styling when you think about it

boreal notch
#

of course I've been using elembic excessively again to experiment :DD

boreal notch
drowsy apex
#

fair enough

#

im making a template which ill need to use very soon and so far i havent used it but it's been a bit of a pain haha

#

i think ill probably experiment with it as well soon

#

cuz its just too convenient to show and set without tons of parameters

native loom
#

Here's a math font size topic, I hope it's appropriate to ask here if it has been discussed as an issue style system changes can fix. The problem is basically this: show math.equation: set text(size: 10pt)
This is for a document where the math font is best matched to text font by reducing equation font size, this should be the standard way to configure it.

The problem is that now the math font size is unchanging regardless of equation context.
What we actually want is to have a "root math font size", like we do for regular text, and still inherit font size changes like text(size: 1.4em). For example: with this style, it breaks equation font size scaling in headings and all other contexts. It's of course a natural consequence of how show rules work.

(The workaround that seems best right now is show math.equation: set text(size: 10em/11) which configures with a ratio to the regular "root text size", this preserves the desired inheritance.)

peak raven
#

coming from discussion channel

#

I think one way is to give set rule access to all of fields that can be content input, like figure.caption, but also figure.supplement, figure.numbering.number or sth similar.

drowsy apex
#

Also not all of them directly translate into some concrete thing, they might be mixed together and stuff..

peak raven
#

Although Typst has documented that things after the keyword show are selectors, I always look it as an element. The show rule give access to that element so that I can override it or modify it, like that of reading via context.

#

Another approach may be like context text.size.get() to read the value? Similar to states.

prime quail
#

That would break a lot of packages

#

I always thought that namespacing and instance access both using . is a bad idea

#

Unfortunately that's how it was done though and I'd say it's a little late to change

drowsy apex
#

the syntax is quite slick, but i agree it might not lend itself too well to "static members" of the type's scope

#

elembic uses a function like get(element).property, though mostly by necessity haha

prime quail
#

Other languages use :: and while I'm personally a fan I know that it's quite arcane to non-programmers

#

I like that it visually sticks out, so you know it's not the same thing

peak raven
prime quail
#

I don't think there is such a case yet

peak raven
#

Is it query ? or show text from figure.caption ?

prime quail
#

I think pg meant that adding more such sub elements makes this more likely

#

Maybe, I'm not sure actuslly

peak raven
#

Ah may be something like:

#show: it => context {
  show figure.caption: set text(..)
  it
}
#

As we continue to have more subelement, using it in show rule with context does create the ambiguity.

#

Especially if that subelement is also an element function

prime quail
#

I think the ambiguity would remain even with different tokens

#

Because it's about whether the expression is a a selector or the value of the get rule

#

At least with consistent usage of the static access token like in other languages I guess

peak raven
#

Hmm, it is obvious that the element after the show keyword should be selectors. However, the ambiguity is that: whether the show rule apply to the subelement or apply to the accessed value of the subelement, imo

#

Like in the case of numbering:

#show figure: it => context {
  show figure.numbering: set text(fill: red)
  it
}

(hypothetical show rule)

#

If we allow figure.numbering to be an element, then this is ambiguous.
Say, the current numbering pattern for figure is "(1)", then the question is: whether the show rule should change the captions to have red text color or show the string "(1)" to be red.

prime quail
#

Yeah I think that's the gist here

drowsy apex
#

but yes, the syntactical ambiguity in context is also a problem

drowsy apex
#

continuing here

#

@peak raven for example, table can receive table.cell arguments. it uses the fields of table.cell , particularly their positions , stroke, colspan and rowspan, to place it.

The show rule only runs after the cell is placed, so the show rule cant change those arguments.

#

the stroke is because it is the table that draws stroke as it has to handle stroke between multiple cells, as well as interaction with rowspans, table headers and footers etc.

#

set rules, however, can change those arguments. set rules are considered before table layout starts.

peak raven
#

So the reason is that show rules give access to the data that depends on interaction between elements, which only can be known when it is laid out. Am I right?

drowsy apex
#

basically show rules are only used to change how an element is displayed.

#

not the element itself , with all of its implications.

#

in the past, set rules inside show rules would work in some cases due to implementation details, but ever since 0.11 introduced proper show-set rules, it was made more consistent , and generally that will no longer work

#

so , you can consider that interactions between elements, but also any code that just needs to read the element's fields in general, will not benefit from show rules

#

they are designed as a content => content function kind of thing

#

not content => (data, content)

#

(nevermind the fact that content is data, in a way, but you get my point... :p)

#

in other words, they have a very local effect

#

the context available, from the perspective of a single sub-element (say, a show rule on figure.caption instead of figure), is too limited...

edgy harness
prime quail
#

Ah of course, I didn';t think of it that way, I don't use dynamic languages often

#

I get that part about ::, I'm definitely not advocating for it here

edgy harness
#

The ambiguity with get and fields on elements is definitely a bit unfortunate

#

Though in practice barely a problem thus far I think

#

I could imagine adding some way to disambiguate down the road

merry patrol
#

Is it desirable to have this show-set rule as part of the standard library?

#show highlight: set text(overhang: false)
```This would improve the default look of highlighted text in justified paragraphs (because currently the end edge of the paragraph appears ragged due to the combination of overhang + highlight). But it's also a somewhat hidden rule that you may not know you should override if you need to disable it for some reason.
edgy harness
#

Might be sensible. Alternatively, the overhang could be ignored for highlighting purposes.

merry patrol
edgy harness
merry patrol
#

That could not be done with a show-set rule though, right?

edgy harness
#

In particular, since compiler built-in show set can also be conditional in code (e.g. only when justification is enabled) which is not possible in Typst code.

merry patrol
#

Doesn't this work?

#show par.where(justify: true): set text(overhang: true)
edgy harness
merry patrol
#

Many default lengths in Typst are expressed in points rather than ems (cancel.stroke, table.inset, etc.). I tend to think almost all default lengths should be in ems instead. Am I missing something or would that be better? If so, I'll be willing to make a PR to change the ones I can find.

edgy harness
#

I think points are nice and simple. I haven't heard anybody complain about this tbh.

crimson elm
edgy harness
#

For cancel, yes

#

It's a bit special in that sense

merry patrol
merry patrol
#

This is also an issue with some plotting packages and it used to be an issue with Curryst but we decided to express all lengths in terms of ems recently.

edgy harness
merry patrol
#

I can do that in the upcoming days

merry patrol
# edgy harness That makes sense. Perhaps it would make sense to start with making a list of all...

Well, it actually wasn't that long to do manually. I may have missed some cases that are hidden behind auto. Overall, I am surprised that so few default lengths are in points.

As of Typst 0.14.0, according to the online documentation. Apart from text.size and page.{width,height} I see no reason not to switch to em lengths.

Point lengths

  • footnote.separator
  • table.{inset,stroke}
  • {table,grid}.{hline,vline}.stroke
  • text.size (duh)
  • math.cancel.{length,stroke}
  • page.{width,height}
  • {cirle,ellipse}.inset
  • {circle,curve,ellipse,polygon,regular,rect,square}.stroke (hidden behind auto)
  • line.stroke
  • line.length
  • {rect,square}.inset
  • stroke.thickness

Em lengths

This list does not include parameters defaulting to auto, which determine their values based on font metrics.

  • {list,enum}.body-indent
  • terms.{separator,hanging-indent}
  • figure.gap
  • footnote.{clearance,gap,indent}
  • outline.entry.fill
  • outline.entry.indented.gap
  • par.{leading,spacing}
  • math.cases.gap
  • math.mat.{row-gap,column-gap}
  • math.vec.gap
  • block.spacing
  • float.clearance
  • polygon.size

Null lengths

This include the length part of relative lengths.

  • {list,enum,terms}.indent
  • par.{justification-limits,first-line-indent,hanging-indent}
  • highlight.extent
  • {overline,strike,underline}.extent
  • text.{tracking,spacing,baseline}
  • math.accent.size
  • math.lr.size
  • math.mat.gap (but AFAIK the default is never used)
  • math.stretch
  • box.baseline
  • columns.gutter
  • grid.inset (documented as (:) btw, which is nor very informative)
  • move.{dx,dy}
  • pad.*
  • page.{header-ascent,footer-descent}
  • float.{dx,dy}
  • repeat.gap
  • circle.radius
  • line.start
  • tiling.spacing
boreal notch
#

Just a wild idea. It would useful to allow transforming functions in set rules to change the current value based on the previous one. As an example:

// A higher level set rule, potentially from a different file/scope/context
#set rect(fill: ..)

// Now whatever the color is, brighten it by 50%
#set rect(fill: clr => clr.brighten(50%))

Under the hood, the new fill will directly be resolved by applying this function to the former fill value. This is conceptionally very similar to parameter folding which is currently already applied to stroke and em values. Precisely, it is like secretly allowing values of type function in set rules and the folding rule for functions is that they are invoked on the previous value.

#

(I posted this in #1175895383600275516 but it rather belongs here I think)

glacial flume
#

What would it do with parameters that already take a function, like heading.supplement, list.marker, or the table/grid stuff? Though I know Laurenz said he didn't like that pattern generally so I'm not sure if that's going to be phased out in the future.

boreal notch
#

It would not be supported with parameters that take a function. For once there aren't many of those in the standard library and also "folding" a function seems like a very niche application to me and by far not as useful as for other types.

edgy harness
edgy harness
#

Regarding #discussions message:

This unfortunately fell a little short in my talk (maybe should have looked at my notes after all), but having these fragments well-documented and well-discoverable is an essential part of the idea. In fact, I would like to add a section with all the built-in show rules to the documentation (including how to write them in Typst even for those implemented in Rust) and this list would be searchable by selector. In addition, every element page would list all built-in show rules relevant to that element. That would also make existing built-in show-set styles discoverable. Autocomplete-wise, I think it would also be possible to suggest the appropriate fragments.

What I like about using ancestry instead of hard-coding fragments on elements is precisely that it decouples the two concerns. The different parts of the default visual representation are, in my opinion, not a concern of the heading itself, but rather of its default show rule (and would be documented as such). If I have a template with a custom headnig show rule, I might decide that my particular heading style would benefit from additional fragments, which users of my template could then also override, without the heading element itself being aware. Or maybe just one fragment of the heading would benefit from additional nested subfragments. In my vision, it's all very mix and match instead of being rigidly defined on the heading element.

maiden wasp
#

I see, that would be great

#

would fragments be shared between elements, e.g., if I override a hypothetical number fragment without ancestry selectors could it affect multiple elements?

#

(bikeshed; 'component' could be a good name if you're not set on 'fragment' as it literally means that which is to be composed)

edgy harness
#

I'm not necessarily set on fragment. Component could theoretically also work, but I would think of components as the larger more meaningful things (e.g. headings, figures) while fragments would be the generic parts they are composed of.

#

Depending on which concrete fragments we end up with and how many, it also doesn't necessarily have to be in a module (I know that some people at the meetup where put off by the fragment. part.)

merry patrol
# edgy harness yes, they would be shared

This is something I still feel weird about. Apart maybe from highly generic things like inner, I don't like that a fragment can have a completely different meaning depending on where it comes from.

maiden wasp
edgy harness
maiden wasp
#

I share Malo's concern, template authors are bound to have (slightly) different interpretations of the meaning/usage of a specific fragment which might respond differently to generic show rules. Or, if the set of provided fragment is too limited (are new fragments allowed?) authors might be tempted to abuse generic fragments like how <div> is often excessively overloaded

prime quail
#

But perhaps that's just up to us then, to find the right granularity

edgy harness
prime quail
#

I don't think people could misuse a hypothetical numbering fragment for example

edgy harness
#

It's all a bit theoretical though without us coming up with a concrete list of fragments that the standard library would profit from.

maiden wasp
prime quail
#

That's bound to happen already

#

I don't think that's inherently a problem of fragments

#

It just happens with package APIs that are developed independently

#

I'm sure we can solve this with time when preferred APIs crystalize

maiden wasp
prime quail
#

That's true, but I don't think this can be avoided

#

After all custom elements are firmly planned and pose the same problem, so I think the solution is still to guide standardization using popular packages

maiden wasp
#

perhaps there could be an selector for packages so that you can exclude misbehaving packages or target only specific packages

rancid fog
maiden wasp
maiden wasp
#

I'm also curious, are fragments intended a step towards custom elements later to be replaced by them, or are they something separate to exist side by side with custom elements?

prime quail
#

I think they won't replace them as elements are supposed to be built from them

#

If they would be replaced by custom elements the composable styling problem of builtin elements would not be solved either

native loom
#

Here is a wild idea / feature request.
Use case: you have an element and want to apply a content/style rule when multiple of those elements appear consecutively. Similar to how multiple cites are grouped together natively in Typst.
Inspired by the fact that we can fake this functionality today by abusing that list.items group together into lists, here's a very short write-up of generalising that to custom grouping behaviour: https://typst.app/project/Re7vSnJDCEAjE88eN8HjDn (review link)
However, I expect Typst devs to have completely different ideas about how grouping rules can be done in the style system. Has it been thought of before? I'm not sure if a good solution belongs to the style system or to custom user types.

umbral sandal
#

(project/R — review, r — read, w — write)