Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[css-text] Non-Collapsible Space #10821

Open
dgp1130 opened this issue Sep 2, 2024 · 31 comments
Open

[css-text] Non-Collapsible Space #10821

dgp1130 opened this issue Sep 2, 2024 · 31 comments

Comments

@dgp1130
Copy link

dgp1130 commented Sep 2, 2024

HTML whitespace collapsing behavior makes it very difficult to directly manage individual spaces rendered in an HTML document. Whitespace collapsing has a few problems today:

  1. Content management systems cannot just blindly pass text written by a non-developer through to an HTML rendering context. If a user types Hello, World in a CMS system, it will be rendered as Hello, World in HTML. The CMS can't really do anything about this to get the document to render as intended by the author without forcing all strings to use white-space: pre;.
  2. white-space: pre; applies to the whole element, but the user might want two spaces in a particular place within that element. For example, maybe I'm one of those people who insists on two spaces at the end of every sentence.
  3.   sounds like an easy fix for "Just add a space here", but forces non-breaking and a forced-width behavior the user may not want (when line wrapped,   always takes up one space of width, even when that's not needed). See the line wrapping behavior of this demo as the viewport shrinks.
  4.   is the unnamed entity for a single space, which sounds like it would be a good alternative given that I really want the behavior for a simple space. However,   is also subject to whitespace collapsing so it can't really solve this kind of problem. This feels especially weird since whitespace collapsing exists as an affordance to developers who want to format their HTML source code differently from the rendered output, but any developer who writes   clearly wants that space to be rendered as-is. Collapsing those spaces goes directly against developer expectations.

This is a shorter, more focused breakdown on some of the problems with whitespace collapsing, however I wrote a whole blog post digging deeper into the complexity at play here which motivated this particular issue and gives additional context and motivation.

I propose a new &ncsp; entity which is treated identically to a regular space, however is not subject to whitespace collapsing. You could write Hello,&ncsp;&ncsp;&ncsp;&ncsp;&ncsp;World and actually get multiple adjacent spaces like Hello, World.

Edit: It seems it is not possible to add new HTML entities so &ncsp; as a syntax won't work. However, the key feature request is a non-collapsible space character. The exact syntax for producing that character is an implementation detail.

This would address the above challenges because:

  1. Content management systems can output &ncsp; where white space is significant.
  2. Developers could use &ncsp; without having to opt-in an entire element into the rules of white-space: pre;.
  3. &ncsp; could serve as a drop-in replacement for existing usages of   but present better line wrapping behavior.
  4. &ncsp; would work in the way I wish   worked and behave in a more predictable fashion.

I'm not 100% which standards body is the right one to own this particular issue, however I filed it here because whitespace collapsing seems to be a part of the css-text standard. In theory, &ncsp; is just an alias for a standard space, one which just gets ignored by the whitespace collapsing algorithm. However, as I understand it based on the current layering of browsers, the HTML parser would resolve &ncsp; into a regular space character, and it would then be impossible for the CSS to disambiguate spaces which originated from &ncsp; entities. Therefore I suspect this would actually require a brand new Unicode character. If one were added for this purpose, then the existing css-text standard likely wouldn't need to be changed at all. However, it feels weird to add one solely to solve an HTML issue like this and I'm not sure that's feasible. What exactly would a non-collapsible space do in a non-HTML context? An alternative approach might be for the HTML parser to convert &ncsp; to a different, user-space Unicode character known by the white-space spec which gets rendered as a standard space but is otherwise ignored for collapsing. I'm not sure that's a great architectural idea, but its the one solution which comes to mind here.

Feel free to move this issue to whichever standards body makes the most sense to evaluate it.

@Loirooriol
Copy link
Contributor

forcing all strings to use white-space: pre;

Rather than pre, you probably want pre-wrap, and possibly combine it with white-space-trim.

the user might want two spaces in a particular place within that element

Do you mean that you want some sequences of spaces to collapse, but not others? Why not just preserve all spaces and use a single one instead of multiple wherever you want to see a single one?

maybe I'm one of those people who insists on two spaces at the end of every sentence

And you want to see both or one?

any developer who writes clearly wants that space to be rendered as-is

When I write   I want it to behave like a normal U+0020 space.

I'm not 100% which standards body is the right one to own this particular issue

  • &ncsp; would be a new HTML entity, so you should propose this to the WHATWG
  • It would need to map to some character. Presumable a new one, so you would need to propose it to Unicode?

And I don't think they will find this much appealing...

@Crissov
Copy link
Contributor

Crissov commented Sep 7, 2024

Yeah, see whatwg/html#5121 and whatwg/html#7071 for instance.

@dgp1130
Copy link
Author

dgp1130 commented Sep 7, 2024

Rather than pre, you probably want pre-wrap, and possibly combine it with white-space-trim.
Do you mean that you want some sequences of spaces to collapse, but not others? Why not just preserve all spaces and use a single one instead of multiple wherever you want to see a single one?

Preserving all spaces in an element is a larger change than strictly necessary to preserve only a particular set of spaces within it and forces the developer to compromise other aspects of how they write their HTML, such as eliminating any indentation or newlines depending on the specific white-space value they are using and the trade offs that requires. &ncsp; would not require developers to make that kind of compromise. They could use any white-space value and just choose to keep any arbitrary space within the element.

Also as mentioned, CMS tools generally can't rely on any specific styles being applied to an element, so any CSS requirement feels like a non-starter for that use case.

maybe I'm one of those people who insists on two spaces at the end of every sentence
And you want to see both or one?

In this example, I would want to see both spaces. If the developer types Good new everyone! HTML supports non-collapsible spaces now. (note two spaces) they will naturally be collapsed into a single space when rendered in HTML. &ncsp;&ncsp; would allow both spaces to be preserved, regardless of the rest of the content or styling for that element.

Yeah, see whatwg/html#5121 and whatwg/html#7071 for instance.

Thanks for pointing that out, I wasn't aware new HTML entities were so difficult to add. I agree this is unlikely to meet the impact needed to justify its addition. An unnamed entity could technically address the same purpose, though I imagine it would be significantly harder to convince the community to prefer an unnamed &ncsp; character over   for those mis-use cases.

It seems I was at least correct that this would require a new Unicode character, but it sounds like it would make more sense to file this in https://github.com/whatwg/html? I imagine we'd need at least some amount of consensus there among HTML stakeholders that this is worth pursing before there would be any hope of getting Unicode on board.

@xiaochengh
Copy link
Contributor

xiaochengh commented Oct 1, 2024 via email

@dgp1130
Copy link
Author

dgp1130 commented Oct 27, 2024

I don't think interleaving no-break and zero-width spaces quite works because no-break spaces have a forced width, meaning they always take up space even when line wrapping would not require it. See point 3 of the original comment, that issue still exists with an interleaving approach.

@rgant
Copy link

rgant commented Jan 26, 2025

Angular introduces it's own special white space entity for similar purposes: &ngsp;

https://angular.dev/guide/templates/whitespace#preserving-whitespace
angular/angular#11679 (comment)

@blm768
Copy link

blm768 commented Feb 7, 2025

I wonder if an element would be a better way to convey these semantics than a character entity. We've got <br>, so it does seem like a <sp> void element with the semantics described here wouldn't be too radical a departure from existing conventions. Seems like that option would be squarely in the WHATWG's domain, which would avoid the need to get Unicode involved.

@Loirooriol
Copy link
Contributor

It's still not much clear to me what exact behavior is wanted here, but you don't even need WHATWG involvement if you use a custom element:

<style>nc-sp::before { content: " "; white-space: pre-wrap }</style>
Hello,<nc-sp></nc-sp><nc-sp></nc-sp><nc-sp></nc-sp><nc-sp></nc-sp>World

or

<style>pre-wrap { white-space: pre-wrap }</style>
Hello,<pre-wrap>    </pre-wrap>World

@dead-claudia
Copy link

dead-claudia commented Feb 11, 2025

I have a better suggestion, one that better integrates with how it's actually done in practice: add CSS properties for significant whitespace control.

  • white-space-before-start: collapse | retain
  • white-space-after-start: collapse | retain
  • white-space-before-end: collapse | retain
  • white-space-after-end: collapse | retain

In each of these, retain retains the space, and collapse trims it.

And of course, three shorthand properties:

  • white-space-inside: <after-start> <before-end>
  • white-space-outside: <before-start> <after-end>
  • white-space-control: <inside> <outside>

In all three, they can accept up to two, but if you only pass one, it intelligently applies to both.

For an easier time with HTML, especially with minifiers and formatters, add trim and notrim global token list attributes to do the same. This desired (partial) UA stylesheet below should explain how they work.

[trim~=before-start i] {
    white-space-before-start: collapse;
}

[trim~=after-start i] {
    white-space-after-start: collapse;
}

[trim~=before-end i] {
    white-space-before-end: collapse;
}

[trim~=after-end i] {
    white-space-after-end: collapse;
}

[trim~=inside i] {
    white-space-inside: collapse;
}

[trim~=outside i] {
    white-space-outside: collapse;
}

[trim~=both i], [trim=""] {
    white-space-control: collapse;
}

[notrim~=before-start i] {
    white-space-before-start: retain;
}

[notrim~=after-start i] {
    white-space-after-start: retain;
}

[notrim~=before-end i] {
    white-space-before-end: retain;
}

[notrim~=after-end i] {
    white-space-after-end: retain;
}

[notrim~=inside i] {
    white-space-inside: retain;
}

[notrim~=outside i] {
    white-space-outside: retain;
}

[notrim~=both i], [notrim=""] {
    white-space-control: retain;
}

Then, if you really wanted to, you could introduce something like a <ncsp> element styled as follows:

ncsp {
    display: contents;
    height: 0;
    width: 0;
    visibility: none;
    white-space-outer: retain;
}

@Loirooriol
Copy link
Contributor

@dead-claudia
Copy link

dead-claudia commented Feb 11, 2025

@dead-claudia That's basically https://drafts.csswg.org/css-text-4/#white-space-trim

Oh, then in that case, we should just add those global HTML attributes to match.

[trim] {
    white-space-trim: discard-before discard-after discard-inner;
}

[trim~=before i] {
    white-space-trim: discard-before;
}

[trim~=after i] {
    white-space-trim: discard-after;
}

[trim~=inner i] {
    white-space-trim: discard-inner;
}

[trim~=before i][trim~=after i] {
    white-space-trim: discard-before discard-inner;
}

[trim~=before i][trim~=inner i] {
    white-space-trim: discard-before discard-inner;
}

[trim~=after i][trim~=inner i] {
    white-space-trim: discard-after discard-inner;
}

[trim~=before i][trim~=after i][trim~=inner i] {
    white-space-trim: discard-before discard-after discard-inner;
}

@VoxelPrismatic
Copy link

but have we considered &zwnj;

@dead-claudia
Copy link

dead-claudia commented Feb 12, 2025

@VoxelPrismatic I could see that working as well:

  • ... &zwnj;<span>... would achieve the same as white-space-trim without discard-before.
  • ...</span>&zwnj; ... would achieve the same as white-space-trim without discard-after.
  • ...<span>&zwnj; ... &zwnj;</span>... would achieve the same as white-space-trim without discard-inner.
  • ... &zwnj;<span>&zwnj; ... &zwnj;</span>&zwnj; ... would achieve the same as white-space-trim: none.

Screen readers do seem to ignore them, so it shouldn't present an accessibility problem.

@VoxelPrismatic
Copy link

VoxelPrismatic commented Feb 12, 2025

I would say that there should be a shorter way to get non-collapsing spaces, perhaps

<span>Hello,</span>&________;<span>World!</span>

which would produce Hello, World!

(each underscore is a space)

@junaga
Copy link

junaga commented Feb 13, 2025

The nerds are right, this is a CSS problem, fix it in CSS.

@sapioit
Copy link

sapioit commented Feb 13, 2025

I would go as far as to say that we would be better off going with the solution recommended in this article, but making it compatible with older HTML versions by simply having to go from <!doctype html> to <!doctype html quoted> or <!doctype html-quoted> or <!doctype html-q> or <!doctype htmlq> or something like that, which to to tell the renderers how to treat the text inside the document.

Unfortunately, I doubt this solution would be implemented, even though it would make using and and debugging things which involve HTML and CSS, much easier.

@rober423
Copy link

rober423 commented Feb 13, 2025

@dgp1130, some questions for you. Going off of @sapioit 's comment, I like that idea better, but I am confused as to why Newlines [would be] banned by default [in this new html syntax and] a quoted newline [would be flagged as a] syntax error. You say it is because it would be easier to forget closing quotes? and that using &lf; would be the replacement for /n in single quoted strings? (remember that some type of newline character is needed in single quoted strings, otherwise we would have to use a #define or something in cshtml to continually refer to a triple quoted newline string when creating strings programmatically) Is there some reason you chose &lf;? Does not quoting it produce errors more reliably than not quoting a /n? Is missing a closing quote that hard to debug?

@dgp1130
Copy link
Author

dgp1130 commented Feb 14, 2025

Seems the blog post got shared around a bit and increased some of the traffic on this particular issue, I'll try to respond to as much as I can here:

I wonder if an element would be a better way to convey these semantics than a character entity. We've got <br>, so it does seem like a <sp> void element with the semantics described here wouldn't be too radical a departure from existing conventions. Seems like that option would be squarely in the WHATWG's domain, which would avoid the need to get Unicode involved.

Personally I'd prefer having an actual space character, rather than a distinct element splitting up multiple text nodes. That feels more intuitive to me and also is more straightforward for tools like CMS which could do basic textual transforms without needing to innerHTML an <sp> tag and having to deal with the security implications of that. Of course if we need a distinct "non-collapsible space" character anyways, then maybe I should just accept that it won't be "just a space" at the end regardless and <sp> might not be all that much worse.

you don't even need WHATWG involvement if you use a custom element:

<style>nc-sp::before { content: " "; white-space: pre-wrap }</style>
Hello,<nc-sp></nc-sp><nc-sp></nc-sp><nc-sp></nc-sp><nc-sp></nc-sp>World

I don't think <nc-sp> would work for SEO / <noscript>, since engines not processing that JS would not know spacing is inserted in that location.

<style>pre-wrap { white-space: pre-wrap }</style>
Hello,<pre-wrap>    </pre-wrap>World

<pre-wrap> </pre-wrap> is probably more viable, since the spaces are at least present in the text. I suspect CMS' would not want to rely on a custom element for their outputs, though a slightly more standard version isn't that far off:

Hello,<pre style="display: inline; white-space: pre-wrap;">        </pre>World

Initial testing does seem to line wrap as I would want. This also works for automated formatters and minifiers, both of which already need to preserve spacing inside <pre> tags and should know not to mess with it. CMS' are still a little weird per the innerHTML point above, I suspect an ideal solution would be plain text / escaped characters, not something which requires a different DOM structure.

However I don't hate this solution. It's awkward and verbose but does preserve the specific spacing required. I'm not sure if there's other behavior here which would be undesirable in this context.

I have a better suggestion, one that better integrates with how it's actually done in practice: add CSS properties for significant whitespace control.

I don't think any CSS properties are a valid solution here. There is no straightforward way for a CSS property to select a specific space to retain and therefore you need to define broad categories of "all leading spacing" or "all internal spacing". Often the developer just wants one in particular to matter.

Also any automated tooling like HTML formatters or minifiers can't know the CSS which will be applied to a particular element and won't know to retain those significant spaces. The solution needs to be expressible directly in the HTML.

but have we considered &zwnj;

I've never heard of this character, but it seems to be a zero-width non-joiner? If I understand this correctly, the idea is that you would place a &zwnj; between two space characters to prevent them from being collapsed together?

Testing this out quickly, it does seem to prevent collapsing behavior but it doesn't really line wrap correctly. Joined spaces can lead newlines, which isn't the intended wrapping behavior. It should line wrap like a standard space.

Image

I would go as far as to say that we would be better off going with the solution recommended in this article

Just to be clear, I'm personally very skeptical such a solution is feasible or would even be a net positive on the ecosystem. The point of that section was to describe a hypothetical alternative in order to highlight some of the shortcomings in the existing design such as the ambiguity around developer intent for whitespace (and even that solution doesn't solve all my complaints!). If we could go back in time and redesign HTML from scratch, I might push for something like that, but given where we are now, I don't think anything there is possible. Even if we declared a new <!DOCTYPE qhtml>, it would still cause an immense amount of churn in the ecosystem and would almost certainly be a cure worse than the disease.

(Also even if qhtml became a thing, there would probably still be value in shipping &ncsp; to existing HTML5 users, so this issue still applies.)

I like that idea better, but I am confused as to why Newlines [would be] banned by default [in this new html syntax and] a quoted newline [would be flagged as a] syntax error.

It's purely a subjective judgment call, there's plenty of different ways to bikeshed the proposed syntax and I'm sure there's alternatives which are just as good, if not, measurably better than my suggestion. It was just the most immediately obvious approach which was easy to explain in the context of that blog post.

I recommend keeping this issue focused on the &ncsp; idea. We can potentially discuss a more holistic improvement in separate issues or venues, though I didn't make any attempt to start such a conversation outside my blog post precisely because I'm not convinced the idea has any real merit.

@Loirooriol
Copy link
Contributor

I recommend keeping this issue focused on the &ncsp; idea.

&ncsp; will not happen: whatwg/html#7071

@dgp1130
Copy link
Author

dgp1130 commented Feb 15, 2025

My intent behind this feature request is more about a non-collapsible space character, how one produces that character is effectively an implementation detail. I agree &ncsp; as a syntax is likely a dead end, but I think an equally valid solution could be something like &#<some-num>;, which I believe would be potentially feasible. I'm open to other alternatives.

I'll update the title and initial comment to make that more clear.

@dgp1130 dgp1130 changed the title [css-text] &ncsp; - Non-Collapsible Space [css-text] Non-Collapsible Space Feb 15, 2025
@VoxelPrismatic
Copy link

VoxelPrismatic commented Feb 15, 2025

@dgp1130 you can use &zwnj; to break your spaces. eg &zwnj; &zwnj; &zwnj;

be warned that all spaces will be visible, even after wrapping.

@dead-claudia
Copy link

dead-claudia commented Feb 15, 2025

@dgp1130 You'd have much better luck asking the Unicode Consortium for that. (And they'd likely reject it if the below works - they'd only approve it if there's no alternative. They're very, very picky with their approvals.)

However, have you considered a zero-width space? The relevant CSS spec explicitly only counts ASCII whitespace (U+0009, U+000A, U+0020, and indirectly U+000D in HTML due to CRLF normalization) as whitespace it can collapse. It doesn't collapse any other code point in Unicode category Zs. So you may be able to get away with using &#x200B;/&#8203;/&ZeroWidthSpace; as your &ncsp;.

@dgp1130
Copy link
Author

dgp1130 commented Feb 15, 2025

@dgp1130 you can use &zwnj; to break your spaces. eg &zwnj; &zwnj; &zwnj;

be warned that all spaces will be visible, even after wrapping.

I considered that in #10821 (comment), but like you mention, the line wrapping behavior isn't right.

However, have you considered a zero-width space? The relevant CSS spec explicitly only counts ASCII whitespace (U+0009, U+000A, U+0020, and indirectly U+000D in HTML due to CRLF normalization) as whitespace it can collapse. It doesn't collapse any other code point in Unicode category Zs. So you may be able to get away with using &#x200B;/&#8203;/&ZeroWidthSpace; as your &ncsp;.

My intent behind &ncsp; is a normal-width space with typical line wrapping behavior (ie. lines do not start with wrapped spaces). &ZeroWidthSpace; is a way of forcing two whitespace characters to not be collapsed together, but then we need to pick what those two characters are.

I tried interleaving with &nbsp; in #10821 (comment), but that has the undesirable line wrapping behavior. I also tried interleaving with regular spaces, but that actually has the same undesirable line wrapping behavior (I'm testing in Chrome, but I'm assuming other browsers match).

A screenshot of the linked demo which shows "Hello" and "World" in a small viewport causing line wrapping. "Hello" is formatted normally, however "World" is effectively indented, with the spaces between the two words having been placed at the start of its line.

If there's a character which can serve as a regular space but avoid this line wrapping behavior, then maybe we can apply "non-collapsible" behavior to it by interleaving &ZeroWidthSpace; characters, but I'm not aware of any such character which meets that criteria.

@dead-claudia
Copy link

@dgp1130 And to be clear, what is the difference between what you need and what white-space: pre-wrap provides?

For clarity, per spec:

  • New lines are preserved
  • Text is wrapped
  • End of line spaces and separators hang: they're ignored for most sizing, but they're otherwise considered significant.
  • Other spaces are preserved

Twitter uses this for their post display, since users like to use spaces for text layout in precisely the same way you talk about with CMS users.

@sapioit
Copy link

sapioit commented Feb 16, 2025

At the risk of repeating myself and being annoying, I have to say: this article explains why white-spaces: pre-wrap is not that good of a solution.

One of the reasons is the inconsistency, since your solution would likely need to be applied as * { white-space: pre-wrap; }, for consistency, and it would still not do a great job. Again, inconsistencies still exist, even if we do that.

That's why I think the <!doctype htmlq> with quote-wrapped text or the 3-quotes-in-a-row on the line before and after the text, to automatically de-indent the text, even if we have to end each line with a space, though that could also be fixed with an :afterLine which could be added for this, and a :beforeLine which could be used for other purposes, like for example for making it easier to show indented code without having to have a lot of spaces in the document, instead being able to have all the text on the same level of indentation and use a tag each time the indentation increases or decreases, said tag also telling the render-engine how much indentation to apply. And that's only one of the reasons for adding :beforeLine and :afterLine, and if <!doctype htmlq> gets made, we would need to discuss about the advantages and disadvantages of making those selectors, too.

@Loirooriol
Copy link
Contributor

<!doctype htmlq> is out of scope for this venue, and has blatant problems like not being backwards compatible so in all likelihood it will be rejected if you propose it to the WHATWG.

Also, the linked article makes untrue statements like "all newlines at the start of a <pre> tag are dropped" (it's its server who is doing that!) so it doesn't give the impression to know what it's talking about.

@dead-claudia
Copy link

dead-claudia commented Feb 17, 2025

Also, the linked article makes untrue statements like "all newlines at the start of a <pre> tag are dropped" (it's its server who is doing that!) so it doesn't give the impression to know what it's talking about.

Minor nit: newlines at the literal start or end of the node's text content are not rendered. But only at those positions and only if they're newlines.

Adding zero-width spaces prevents this with no change to alignment. And zero width spaces should be treated as space by any functioning screen reader. This does still impact what text is copied to the clipboard, though.

@junaga
Copy link

junaga commented Feb 18, 2025

I would go as far as to say that we would be better off going with the solution recommended in this article, but making it compatible with older HTML versions by simply having to go from <!doctype html> to <!doctype html quoted> or <!doctype html-quoted> or <!doctype html-q> or <!doctype htmlq> or something like that, which to to tell the renderers how to treat the text inside the document.

Unfortunately, I doubt this solution would be implemented, even though it would make using and and debugging things which involve HTML and CSS, much easier.

@sapioit you are insane if you believe this is reason enough to touch <!DOCTYPE>.

the most likely scenario in which <!DOCTYPE> is patched, would be MDN and WHATWG joining into the HTML6 community: <!DOCTYPE html6>, and nothing else comes close.

i encourage the public paritcipation from common developers in spec authorship. but copy pasting opinions from entertainment-coding-youtube-channels, is not how you do it. this is the video in question and the reason why this issue is getting so much traction.

@sapioit
Copy link

sapioit commented Feb 18, 2025

That video might be why people are getting here, but that doesn't mean they don't have issues with the problems presented in said video, and in that link I linked in two previous comments.

Well, if the doctype isn't changed, that doesn't mean there can be no solution. For example, adding quoted to a tag could be the solution. That way, you could have most of the website using the old way, and only adding the quoted to the tags which you want to be rendered in the new way. And if it gets implemented, I'm sure there will be JS libraries for adding this functionality to browsers old enough to not have the quoted feature. And the reason I'm sure of that is because this is already the case for LESS (which is both a file format, a script for a file type which gets compiled from LESS into CSS).

@Loirooriol
Copy link
Contributor

Again: all of these ideas for HTML are out of scope here. Propose them to the WHATWG if you want (but the likelihood of being accepted still seems negligible to me).

@junaga
Copy link

junaga commented Feb 19, 2025

@sapioit again: whitespace is controlled in CSS-land. we need a CSS solution. that can then be burried in reset stylesheets. that's how things are done here. the authors and theos proposal for radical change is brave in itself, but without an ounce of grace it was determined to fail.

you want to fix the web, thank you, but not like this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests