Active-CSS / active-css

The epic event-driven browser language for UI with functionality in one-liner CSS. Over 100 incredible CSS commands for DOM manipulation, ajax, reactive variables, single-page application routing, and lots more. Could CSS be the JavaScript framework of the future?
https://activecss.org
Other
42 stars 7 forks source link

Allow HTML & CSS file imports for components #222

Closed bob2517 closed 2 years ago

bob2517 commented 2 years ago

As an alternative to html {} inside components, have a html() parameter in the component statement that imports html file in the html {} location without having to do manual ajax and {$STRING} rendering.

dragontheory commented 2 years ago

This is going to be awesome!

Will this handle CORS requests?

bob2517 commented 2 years ago

Hmmm... good point. It will probably need additional options. Have you been running into any CORS issues, or are just thinking ahead?

It should be fine for same-server requests if there has not be any additional security added on the back-end. All the ajax in the core runs through the same code, so it should be fine if your ajax normally works and the HTML files are being stored on the same server.

Hang on a sec though. Are you running a server? Or did you want to test this on a local computer using file://?

dragontheory commented 2 years ago

Have you been running into any CORS issues, or are just thinking ahead?

No problems. Just thinking ahead because I've had issues with that in the past.

I am running on a local server but I am also demo'ing in CodePen, which doesn't allow file hosting.

It would certainly be nice to be able to just drag/drop index.html into a browser on my local machine and have everything just work out-of-the-box, but please don't hold up dinner for desert on my account! lol

bob2517 commented 2 years ago

Ok - I'll get it working with ajax first and we'll try it out and see what it can and can't handle.

There is an alternative solution for offline websites with the command load-as-ajax, which I use for the offline version of the docs site to simulate ajax calls (apologies because that command is not very well-documented). You essentially put HTML into template tags and simulate an ajax request on those to "load" them. That could be tied into this new method. Will take a look at that once the ajax stuff is done.

bob2517 commented 2 years ago

Note to self: immediately after this ticket is done, do the issue where it talks about using variables, etc. inside server-side HTML, as that will be probably the next thing that crops up regarding this method. Most of the components on the docs site utilise variables in the HTML... in fact, I can't seem to find one that doesn't.

Looking at that some more, that should really be part of this upgrade, so don't do a release until that is also in place - just push it onto the branch.

bob2517 commented 2 years ago

Also needs css option for pulling in CSS, and also the observe option from create-element to really get rid of unnecessary typing.

I'm not going to complicate this issue by going into too many specifics, but the final result for a basic component will look something like:

@component clock-component html("/examples/clock.html") {
}

That would pull in /examples/clock.html whenever \\ was found on the page.

It's more complicated that this, as there are lots of things you can do with components that I need to account for, like shadow DOMs, private scopes, variables, etc. but that's the basic starting code for this upgrade. Most of the code for this is written already - it's more a case of sorting out syntax rules and stuff like that and tying in the ajax. Once that is all done everything should just "magically" work.

bob2517 commented 2 years ago

I'm going to leave this as my test case for now. I'll come back and integrate create-element into the component syntax when the html option is working, as I said I would get that done this weekend and it's an easier immediate target. That will include full event and scoping capability.

body:init {
    create-element: clock-component clockComponent;
}

@component clockComponent html("/examples/clock.html") {
...
bob2517 commented 2 years ago

Note to self: it's probably going to need caching options like ajax too. For now, whenever the component is run, it will always fetch dynamically. But I may reverse this and put in a no-cache option as I think caching will probably be the most wanted default. There's no point going multiple times to the server for the same content, but it could be dynamically generated HTML, so the future no-cache option is indicated.

bob2517 commented 2 years ago

Note to self: having a "template(#myHTML)" style of option will solve pulling in HTML from the page as an alternative for offline websites using components like this.

bob2517 commented 2 years ago

Note to self: Add the @function issue to the 2.10.0 release. The example I'm using to set this up in the core has a custom event being triggered that would be better off as an ACSS function call. The syntax for that currently looks a bit weird.

bob2517 commented 2 years ago

Note to self: Nested components using this method will need to wait until the HTML loads, before it can find out what to do with the inner component, because there may be another one to render inside it. So this method is going to be slower than other methods because of the round-trips to the server necessary for the case of nested components. To mitigate this liability, I'm going to put in an optional option to load and cache inner HTML for components as part of config loading. That way, there shouldn't be a noticeable stagger when rendering components dynamically. It will, however, affect the time that it takes to load the initial config. I don't see a cleaner way of doing that without the internals getting overly complicated trying to deal with the inevitable race conditions of pre-loading HTML for components at the same time as trying to render it. Having a "bundle HTML" type of command could accept one file containing multiple HTML components, but then that's a step towards complexity that doesn't make a lot of sense when you could just use the upcoming template() method to pre-render all the HTML you need in your main file, which would process quicker anyway.

bob2517 commented 2 years ago

There will be a "pre-load" option in the @component syntax (not right now - later), which will preload the HTML file in a component at the point of config load, rather than at the point when the component is rendered. If pre-load is not specified, HTML will be loaded dynamically and cached. There will also be a "no-cache" option (not right now - later) which will force a fresh reload of HTML whenever the same component gets rendered.

bob2517 commented 2 years ago

Note to self: Setting up options in the core, "preload-files" and "nocache-files" are stored but don't do anything yet. These could change. All other options are also set up ready to be implemented.

dragontheory commented 2 years ago

Was reading through your comments and it occurred to me that someone may want to call more than one HTML file or mix the different ways to get HTML.

Something like this?

htmlFile(/myfile.html)
html(selector containing HTML already rendered)

/* and inside the component itself if neither of those are used: */
html {
<p>Would this paragraph appear after the ajax html file?</p>
}
bob2517 commented 2 years ago

Ok, point noted. Let's keep the syntax for this simple for now, and then see what is indicated in practice when using them. A future upgrade should be a bit more obvious once this basic syntax is sorted out.

Only one will be allowed for now. It would be really easy to add the others in too, but the rules would need to be properly worked out, so we really need an actual use case to work out what is needed, if anything.

bob2517 commented 2 years ago

I'm re-jigging the syntax for html() itself to allow for the ajax options, like post, get, nocache, etc. I'm not far off getting this working now. Should have something by the end of the day hopefully.

bob2517 commented 2 years ago

Note to self: Error handling for failing HTML load will need to be set up next weekend with a handling which has yet to be worked out.

bob2517 commented 2 years ago

Got the first draft done, with the html parameter working (it seems).

Going to put onto the branch in a moment and see what happens next... famous last words.

Basic syntax is:

@component clockComponent html("/examples/clock.html" get) {
@component clockComponent html("/examples/clock.html" post) {

It allows all the parameters of the ajax command within the html option. So when I add more to the ajax command, it will reflect in the html option.

I've not done the css() option or any of the other options yet. Variable scoping and events will work though.

The create-element command is still needed to trigger off the component.

This is a good first step though, and in theory was the trickiest one. So it bodes well for the remainder of the options.

bob2517 commented 2 years ago

Just tested with reactive variables in the HTML file. It even works with that. I disallow it for regular HTML loaded with ajax. I just blew my own mind :) Not sure whether I should leave that in or not - I need to think a bit more about any potential implications.

Retiring for the eve now. May resume during the week, otherwise 't will be next weekend for the rest.

bob2517 commented 2 years ago

The test case is this below. There is still room for improvement - @function will replace the use of the custom event, and the create-element instruction will disappear when this issue is complete.

The html file contains:

<div>Time: {{clockTime}}</div>

And the currently working ACSS is:

body:init {
  create-element: clock-component clockComponent;
}

@component clockComponent html("/examples/clock-component.html" get) private {
  &:beforeComponentOpen {
    ~clock { trigger: tick; }
  }
  ~clock:tick {
    var: clockTime new Date().toLocaleTimeString() every 1s label clockTick;
  }
  div:click {
    @if has-class(self .clockOff) {
      remove-class: .clockOff;
      ~clock { trigger: tick; }
    } @else {
      cancel-timer: clockTick;
      add-class: .clockOff;
    }
    add-class: .clockPress;
    remove-class: .clockPress after 100ms;
  }
}

which will become something like this leaner version:

@component clock-component html("/examples/clock-component.html" get) private {
  &:beforeComponentOpen {
    func: tick;
  }
  @function tick {
    var: clockTime new Date().toLocaleTimeString() every 1s label clockTick;
  }
  div:click {
    @if has-class(self .clockOff) {
      remove-class: .clockOff;
      func: tick;
    } @else {
      cancel-timer: clockTick;
      add-class: .clockOff;
    }
    add-class: .clockPress;
    remove-class: .clockPress after 100ms;
  }
}

with css (loaded in the usual way) being:

div {
  background-color: #699869;
  color: #f3fff3;
  width: 340px;
  height: 100px;
  margin: 30px 0 40px 20px;
  padding-top: 44px;
  font-size: 36px;
  text-align: center;
  border-bottom: 4px solid #2f662f;
  border-radius: 50%;
  transform: skewX(15deg);
  box-shadow: -13px 8px 12px 1px rgba(0,0,0,0.16), 0 2px 10px 0 rgba(0,0,0,0.12);
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  transition: border-width 100ms linear, transform 100ms linear, box-shadow 100ms linear;
}

div.clockPress {
  background-color: #74a074;
  color: #e7f7e7;
  transform: skewX(12deg);
  box-shadow: -4px 5px 12px 0 rgba(0,0,0,0.16), 0 2px 10px 0 rgba(0,0,0,0.12);
  border-width: 1px;
}

div.clockOff {
  background-color: #a2a2a2;
  border-color: #4d4d4d;
  color: #bfbfbf;
}
bob2517 commented 2 years ago

Works with the shadow DOM option too. The CSS link tag can be in the HTML or it can be embedded. I'll still do the css() option, just in case the developer wants that in a separate file too from the HTML file. That way, you could have different CSS for the same component. Separation of concerns. Dragon theory possibly gives the thumbs up for that.

dragontheory commented 2 years ago

You know it bro!

╱╱┏╮ ╱╱┃┃ ▉━╯┗━╮ ▉┈┈┈┈┃ ▉╮┈┈┈┃ ╱╰━━━╯

bob2517 commented 2 years ago

Next, implement the css() option. Both html and css files will need to be loaded before the component is rendered, so we don't get unwanted flicker of CSS when a component is drawn without its CSS available.

Shadow DOMs can actually have their own link tags in the HTML, which is a different technique. It would potentially be slower to render this way though, as there would be a definite stagger in calling the server. Only when the HTML was rendered would the CSS be fetched and subsequently rendered, which personally I think is a bit of a raw prawn.

I'll get css() working with regular non-shadow DOM components first and see where it leads with regards any additional shadow DOM technique. If I just embed both at the same, it should solve the shadow DOM flicker issue anyway and be a better technique than using the native solution of a separate link tag within shadow DOM HTML.

bob2517 commented 2 years ago

Well, that was easy to set up. Works with shadow DOM components too - no CSS flicker. It waits until both HTML and CSS files have been retrieved before the component is drawn.

Will commit this in a sec and then move onto getting it working with HTML templates, which will provide a faster render time by using HTML that is already rendered on the page inside standard template tags.

This is a bit of a game changing one, this one about importing HTML and CSS. It avoids getting into messy HTML and CSS templating inside of ACSS config, which is something I've never been particularly happy about. Thank you Dragon Theory for requesting it.

bob2517 commented 2 years ago

The css() option is now on the branch. It doesn't have to be used, but it's there anyway. It's a good solution if it's a shadow DOM component and you want to keep your CSS for it in a separate file.

It's also good if you want to have multiple CSS versions for the same component. Like styling a particular component for a client's website without hacking the main code. It's where re-usability becomes practical.

bob2517 commented 2 years ago

I'm splitting the use of pre-rendered template tags to store content, to facilitate separated pre-rendering when using the technique of compiling pages on the server.

Where ajax uses html(filename) and css(filename) options, templates will have corresponding html-template(template tag selector) and css-template(template tag selector) options.

These template options will be able to work alongside html() and css() options for maximum flexibility, but they won't be rendering together in any particular order. We are simply wanting to output HTML content together with CSS content (which gets put into a style tag automatically by the core). So the recommended combinations will be:

html() + css() html-template() + css-template() html() + css-template() html-template() + css()

You could, of course, just put the CSS into a style tag directly into any imported HTML, and not bother with the css() or css-template() options.

By default, ACSS variables will not be allowed in these imported files. This allows for safer rendering from database content on the server. Eg. a user may submit a blog post containing some ACSS variables. We don't want these evaluating by default if then later rendered in code from a developer who forgot to put the unplanned-for option "disallow-vars" into their ACSS component. It should be the other way around. The developer should specifically allow the use of variables in components, so to allow variables, "allow-vars" needs to be an option in the component, which puts the control in the hands of the developer by enforcing a conscious decision to allow ACSS variables to be recognised as such in their components. They can then manually escape ACSS variables at pre-render time if using database content inside component html.

dragontheory commented 2 years ago

Thank you Dragon Theory for requesting it.

Many will benefit from your time and effort. Thank YOU for building it!

bob2517 commented 2 years ago

No... thank YOU! Lol :)

bob2517 commented 2 years ago

Note to self:

Put a warning in the docs. When using ACSS config in an iframe (like I'm doing for my tests) and the HTML or CSS is stored in templates outside of the iframe, the parent selector needs to be employed in the html-template option. Eg. "parent -> #myHTMLTemplate". It took me a while to work out what was going on... Now that I know what's going on, I'm just going to put the template into the iframe itself, where the action is happening.

bob2517 commented 2 years ago

Also note in the docs for this, that CSS that comes from a template should have a style tag in the template, otherwise it isn't proper valid HTML.

bob2517 commented 2 years ago

html-template() and css-template() are now done, and I'm about to do a commit for those.

I've set it up now so that if there is additionally an html { } inside the component itself, then that will render above any other html or css that is loaded via component options.

Time will tell what implications that has... I'm leaving that there for now to see what happens.

It might be useful if someone just wants to separate out the CSS.

bob2517 commented 2 years ago

html-template() and css-template() options are now on the branch. These would look like this in the HTML:

<clock-component></clock-component>

<template id="clockTemplateHTML">
  <div>Time: {{clockTime}}</div>
</template>

<template id="clockTemplateCSS">
  <style>
    div {
      background-color: #699869;
      color: #f3fff3;
      width: 340px;
      height: 100px;
      margin: 30px 0 40px 20px;
      padding-top: 44px;
      font-size: 36px;
      text-align: center;
      border-bottom: 4px solid #2f662f;
      border-radius: 50%;
      transform: skewX(15deg);
      box-shadow: -13px 8px 12px 1px rgba(0,0,0,0.16), 0 2px 10px 0 rgba(0,0,0,0.12);
      cursor: pointer;
      user-select: none;
      -webkit-tap-highlight-color: transparent;
      transition: border-width 100ms linear, transform 100ms linear, box-shadow 100ms linear;
    }

    div.clockPress {
      background-color: #74a074;
      color: #e7f7e7;
      transform: skewX(12deg);
      box-shadow: -4px 5px 12px 0 rgba(0,0,0,0.16), 0 2px 10px 0 rgba(0,0,0,0.12);
      border-width: 1px;
    }

    div.clockOff {
      background-color: #a2a2a2;
      border-color: #4d4d4d;
      color: #bfbfbf;
    }
  </style>
</template>

With the component looking like - as it stands currently before further work on this:

body:init {
  create-element: clock-component clockComponent;
}

@component clockComponent
    html-template(#clockTemplateHTML)
    css-template(#clockTemplateCSS)
    private
  {
  &:beforeComponentOpen {
    ~clock { trigger: tick; }
  }

...etc

}

Next will be setting it up so that the create-element command is not needed (the observe() option is needed to do a proper job on that, as that allows the passing of attributes into the component, which is the standard way of passing attributes into components for custom elements, even if it is a bit long-winded). I need to do some household chores now which I've been putting off all weekend, and will hopefully come back to this later on.

Note to self: Don't forget the "allow-vars" option.

bob2517 commented 2 years ago

Note to self: Re implying create-element within component declaration: just call create-element during a pre-user init event if the component name is at least double-hyphenated. Speed implications for pre-init events will be the same as a create-element in body:init() regardless. If the component name is double-hypenated, assume that the double-hyphenated component name is also the tag name. Just make it clear in the docs that component names, if they are being used in a non-custom element way, should be camel-cased with no hypen. Component naming convention has been implied by the ACSS docs, but not officially stated. Custom elements should always be double-hypenated per the HTML spec, and that definitely is mentioned in the docs. Internally, when rendering the component, it needs a name, and I think the component name can be the same as the custom element tag name - it should be fine with hyphens - may need tweaking if not currently.

bob2517 commented 2 years ago

I'm going to complete this issue today, if it's the last thing that I do!

bob2517 commented 2 years ago

Next to tackle - official creation of the custom element as part of the component syntax.

So instead of a component looking like:

body:init {
  create-element: clock-component clockComponent;
}

@component clock-component
    html-template(#clockTemplateHTML)
    css-template(#clockTemplateCSS)
    private
  {
  &:beforeComponentOpen {
    ~clock { trigger: tick; }
  }
...etc
}

It will now allow a more direct syntax:

@component clock-component
    html-template(#clockTemplateHTML)
    css-template(#clockTemplateCSS)
    private
  {
  &:beforeComponentOpen {
    ~clock { trigger: tick; }
  }
...etc
}

I'll expand the component reference syntax to allow for {|clock-component}. That way we shouldn't need the camelCase reference at all. But that will still be allowed for backward compatibility and also for non-custom element components.

After that, the observe() option from create-element for observing attribute changes as per the native web component rules will be placed directly into the @component statement, so that the correct setups are done behind the scenes.

So in summary: 1) Get the element working as a name in @component. 2) Get the component reference variable "{|component}" allowing a dash in the name. 3) Add observe() into the @component statement. 4) Tie auto-custom element creation into it all so everything gets set up prior to the component being used.

dragontheory commented 2 years ago

After that, the observe() option from create-element for observing attribute changes as per the native web component rules will be placed directly into the @component statement, so that the correct setups are done behind the scenes.

Ensuring syntax accuracy via automation while not obfuscating contextual meaning. I like it!

dragontheory commented 2 years ago

If the component name is double-hypenated, assume that the double-hyphenated component name is also the tag name

This would be nice!

Component naming convention has been implied by the ACSS docs, but not officially stated.

I did what most DEVs do I think. Just copied/pasted the pattern you had in your documentation or examples you've given me. I think that is a strong convention.

I think the component name can be the same as the custom element tag name

That can definitely be assumed in my case with naming conventions. Seemed intuitive to me to leave a breadcrumb path in the logic.

dragontheory commented 2 years ago

Next to tackle - official creation of the custom element as part of the component syntax.

So, in my example, this would eliminate the need for my components.acss, that ties hyphenated HTML elements to ACSS equivalents? The association would be implied?

body:init {
  create-element:
    list-app listApp,
    list-dataset listDataset,
    list-details listDetails,
    list-pagination listPagination,
    list-results listResults,
    page-container pageContainer,
    page-header pageHeader,
    panel-nav panelNav,
    panel-intro panelIntro,
    panel-main panelMain,
    panel-splitter panelSplitter,
    panel-aside panelAside,
    panel-aside2 panelAside2,
    page-footer pageFooter;
}
bob2517 commented 2 years ago

Yep. You will just need to rename your components to match the tag names and then you won't need this file at all.

dragontheory commented 2 years ago

I think that could take the event-driven method to the next level though - checking both the state of the DOM and anything else that can be set up in an @if statement, in just one @observe event statement.

Need some feedback. DT?

Timely that you are addressing this...

This is getting down into the weeds a bit but that is where we need to go if we are to make sure all this work with real world scenarios.

In my example, I have styles and layouts pre-setup when key classes or a sequence of classes are in the <body>. Those classes are added to or removed from <body> when certain DOM content changes, via ACSS @observe.

That's cool and all, but the piranha in the water here (to borrow a metaphor) is that not ALL changes in the DOM layout are due to content changes.

For example, the class .aside2 is added to <body> when the <user-name> DOM element is clicked, allowing <panel-aside2> (user settings panel) to be visible.

While the other layout classes can only be added/removed conditionally and in workflow (left to right) sequence, the .aside2 class can be added/removed from <body> at any time.

As the number of different layouts and combination of layouts grow, I forsee this logic both on the CSS and the ACSS side quickly getting out of hand ... so to speak.

So, this will be really helpful, me thinks.

bob2517 commented 2 years ago

Lol! meme.

Great feedback, thank you. That's good enough for me - I'm implement it and see what happens.

bob2517 commented 2 years ago

The create-element command is no longer needed now (for components) on the branch.

For example, a shadow DOM clock component can just be written like this now. HTML:

<basic-clock></basic-clock>

with ACSS:

@component basic-clock private shadow {
    &:beforeComponentOpen {
        var: clockTime new Date().toLocaleTimeString() every 1s;
    }
    html {
        <div>Time {{clockTime}}</div>
    }
}

To make this possible, the observe() option from the create-element command (for monitoring attribute changes in line with the W3C spec) is now accepted directly in the @component statement.

The create-element command is still useful if you want to officially create elements and are not using components, so backward-compatibility is maintained and create-element will not be deprecated.

I also fixed an issue with the parent selector when used in combination with a complex selector. There isn't an official W3C spec for this, so observation based on intuitive use has been determining its expected behaviour. This appears to be the most intuitive use. So "host < div p" will find the component host element, then find the closest div, then go back down the DOM looking for the next p tag, ie. following the CSS combinator rules. The following are also now supported to give the full predictable range of combinator support, "host < div > p", "host < div + p" and "host < div ~ p". This is now on the branch.

bob2517 commented 2 years ago

The only thing left on this issue is to add "accept-vars" as an option for the component, and remove the current default behaviour of allowing ACSS vars. ACSS vars will only be automatically rendered as such if accept-vars is an option in the component. This helps prevent variable injection from user input rendered from a database on the server.

This does not affect backward compatibility for any release prior to 2.10.0, but it does change behaviour on the branch, so the "accept-vars" option will need to be added for 2.10.0 components containing variables in external files or templates if this new branch feature has been employed.

dragontheory commented 2 years ago

The create-element command is no longer needed now (for components) on the branch.

To confirm, I no longer need components.acss because all it has is this code in it?

body:init {
  create-element:
    list-app listApp,
    list-dataset listDataset,
    list-details listDetails,
    list-graphics listGraphics,
    list-layouts listLayouts,
    list-pagination listPagination,
    list-results listResults,
    list-themes listThemes,
    page-container pageContainer,
    page-header pageHeader,
    page-search pageSearch,
    panel-nav panelNav,
    panel-intro panelIntro,
    panel-main panelMain,
    panel-main-count panelMainCount,
    panel-splitter panelSplitter,
    panel-aside panelAside,
    panel-aside2 panelAside2,
    page-footer pageFooter;
}
dragontheory commented 2 years ago

For example, a shadow DOM clock component can just be written like this now. HTML:

<basic-clock></basic-clock>

Beautiful in it's simplicity.

Making the changes to my project to test.

bob2517 commented 2 years ago

The create-element command is no longer needed now (for components) on the branch.

To confirm, I no longer need components.acss because all it has is this code in it?

body:init {
  create-element:
    list-app listApp,
    list-dataset listDataset,
    list-details listDetails,
    list-graphics listGraphics,
    list-layouts listLayouts,
    list-pagination listPagination,
    list-results listResults,
    list-themes listThemes,
    page-container pageContainer,
    page-header pageHeader,
    page-search pageSearch,
    panel-nav panelNav,
    panel-intro panelIntro,
    panel-main panelMain,
    panel-main-count panelMainCount,
    panel-splitter panelSplitter,
    panel-aside panelAside,
    panel-aside2 panelAside2,
    page-footer pageFooter;
}

Yes, that's correct. You can remove those. All you then need to do is make sure the component names match the element, so for example:

@component pageFooter html(...) {

becomes:

@component page-footer html(...) {

And then do that for all your components.

I agree, it does look a lot neater. It took a while to get that syntax right...

bob2517 commented 2 years ago

Almost there for a release soon, then I'll tackle your page splitter. The target for the 2.10.0 release with the existing issues finished is 10th April.

dragontheory commented 2 years ago

Yes, that's correct. You can remove those. All you then need to do is make sure the component names match the element,

Made the changes and they are working great! Thank you!

dragontheory commented 2 years ago

Question about cookies...

I don't see them in the directory I set for them. Can any directory be set for cookies?

bob2517 commented 2 years ago

Cookies are not stored in directories - the directory path option is more like a scope for the cookie - the cookie itself is kept in memory. You can inspect the values of cookies from DevTools (in Chrome - under the Application tab, in the Storage section).

Try setting a cookie in there - set path to ('/') to make sure that your whole website can get to it. Then have a look in DevTools and see if it's there. You might need to refresh the page.

What do you want to then do with the cookie, out of interest? There are some conditionals in ACSS for checking for cookies, but there's probably a case for adding more cookie functionality.

Note: As an aside, server-side session variables are however, stored somewhere. Sometimes they are stored in memory for speed, but a lot of times they are stored in files on the server (outside of your web directories) and they can even be stored on a database instead. But front-end cookies are never stored on your server.