Jump to content

Recommended Posts

  • Moderators

Browsers are certainly talented as multi-language processors. They have to understand html, css, javascript, svg and lately web-assembly as a fifth language. XML and Json are format specifiers, but could be borderline categorised as languages as well.

Smart elevates javascript to great heights, but sort of leaves the other ones at the periphery. Especially css.

Css can be used for a) component styling including colours, fonts etc, b) visual layout of pages/forms and reacting responsively to screen size and orientation and c) interactivity in various forms. Some of this overlaps with the js domain. Smart basically uses css for a) but not for b) or c).

Looking at various css frameworks out there, I would categorise these as

  • on one end of the scale there are many UI kit frameworks where the emphasis is on components - buttons, accordions, cards etc. Sometimes as css stand-alone files, sometimes in conjunction with js. Most of these also have functionality for layout purposes, usually based on flexbox or internal grid structures.
  • on the other end of the scale there are frameworks which forego the component focus, but instead provide a large number of elementary styling functions, which can be applied to any component.
  • and many which occupy a space in between

This post is about the open source Tailwind framework, belonging in the second category above, just to see how that would work in the Smart environment.

Tailwind comes in 2 flavours : a static framework which can be accessed locally or from a cdn, and a more dynamic version. The latter can be tailored dynamically for any specific project and basically needs to be part of the build toolchain. To keep it simple I used the static version.

Change the res/app.css reference to the tailwind css file (either just replace it or change the reference in the index.html file). Now any component can be made to use this styling. Because this is not more (or less) than a list of individual styling functions, the use boils down to defining what you want. Tell a story as in "give me a button with a green background, which turns to blue on hover, has rounded corners and white text and a shadow effect which makes it seem floating". In code :

  W3Button1.handle.className := "bg-green-500 hover:bg-blue-700 text-white rounded shadow-2xl";

The second button in the demo project (see below) has a different story (className sequence), which gives it a totally different look and feel.

I kind of like this approach, it works very well with any component, thus also with the built in Smart components.

It is a bit of a drag specifying these className sequences by hand, so it would be nice if this would be supported in the IDE. There are after all only a small number of categories of these elementary styling bits : backgrounds, typography, borders, spacing, sizing, svg, interactivity and effects. And a relatively limited list of functions per category (bg-green-500 means a green background, 500 is the fifth shade in the green colour scheme. So there will be a bg-green-600, a bg-blue-200 etc). Doable I think.

Apart from these styling functions, Tailwind also has a number of layout functions. Smart has its own layout mechanism, however the Tailwind functions can be used as well. See the 'card' component in the demo.

Demo and source

 

 

Link to post
Share on other sites
  • Moderators

Wow, you guys are funny.

The question I had was : what is the easiest way to style Smart components.

Smart ships with a couple of standard stylesheets, or themes, so styling is a matter of selecting one of these. To change the styling of a specific component in a theme, just change the css specifications in the stylesheet, either manually or through code.

A different topic altogether is to develop new themes. It is not a simple task to f.i. develop a Metro theme stylesheet which covers all of the current Smart visual components.

That's why I had a look at externally available css frameworks, to see if any of those would help with developing themes, or even work within the Smart development environment and if they are useful to begin with. I focused on one particular framework : Tailwindcss 

One of the problems with themes is when it becomes necessary to deviate from it. Say a project has chosen the default theme (default.css), but for some legitimate reason somewhere in the project a standard component has to be styled differently. Example : colour a button green to amplify a positive choice and colour it red to amplify dangerous choices ("do you really want to download these files from an unknown source ?" and then display a green "No" button and a red "Yes" button).

It's not that simple to make this actually happen.

Lets say we change the styling of the green button (based on the standard button), and opt to do that in code :

  var Button2 : TW3Button := TW3Button.Create(self);
  Button2.SetBounds(20,120,150,40);
  Button2.Caption := 'No';
  //
  Button2.handle.style.background := '#48bb78';          //Button2.Color := clTeal;
  Button2.handle.style.border := '1px solid #48bb78';    //Button2.Border.Color := clTeal;
  Button2.handle.style.borderRadius := '0px';            //Button2.borderradius := 0;
  Button2.handle.style.color := 'white';
  //
  //simulate hover
  Button2.handle.onmouseover := procedure(event: variant)
  begin
    event.target.style.background := 'orange';
  end;
  Button2.handle.onmouseout := procedure(event: variant)
  begin
    event.target.style.background := '#48bb78';
  end;

works sort of ok, but requires a bit of effort

Alternatively we can change the stylesheet, so if we opt to do that for the red button then this requires

  var Button3 : TW3Button := TW3Button.Create(self);
  Button3.SetBounds(200,120,150,40);
  Button3.Caption := 'Yes';
  //
  Button3.TagStyle.Clear;
  Button3.TagStyle.Add('TW3Button TW3ButtonRed');
  var cssProperties := 'background-color: red;';
  browserapi.document.styleSheets[0].insertRule(".TW3ButtonRed { " + cssProperties + " }", 0);
  /*
  .TW3ButtonRed {
    background-color: red;
  }
  */
  cssProperties := 'background-color: orange;';
  browserapi.document.styleSheets[0].insertRule(".TW3ButtonRed:hover { " + cssProperties + " }", 0);

either in code or adding the .TW3ButtonRed and the .TW3ButtonRed:hover manually to the stylesheet

Again, works but requires some effort

Using the Tailwind css framework, the above would be simplified to

  //green
  var Button4 : TW3Button := TW3Button.Create(self);
  Button4.SetBounds(20,220,150,40);
  Button4.Caption := 'No';
  Button4.handle.className := "bg-green-500 hover:bg-blue-700 px-6 text-white rounded shadow-xl";

  //red
  var Button5 : TW3Button := TW3Button.Create(self);
  Button5.SetBounds(200,220,150,40);
  Button5.Caption := 'Yes';
  Button5.TagStyle.Clear;
  Button5.TagStyle.Add("bg-red-500 hover:bg-blue-700 px-6 text-white rounded shadow-xl");

 

Button1 : standard styling
Button2 : changing standard styling in code
Button3 : changing standard styling using css
Button4/5 : using Tailwind

image.png

 

I've probably confused everyone even more now

Anyway, these are my thoughts : Tailwind has the advantage that it can style any of the standard Smart components. As such it could be used to make up a f.i. Metro theme. Still a significant task, but doable. Apart from that, I actually quite like Tailwind to style components on a need-to-do basis, their styling functions make the standard SMS buttons, panels, editboxes, memos etc look really good and modern.

(A card btw is just a panel with a certain layout, where usually multiple of these panels appear on a form or page.)

 

Link to post
Share on other sites
  • Moderators

Easy to use...

Suppose we want to develop a Metro theme
To take buttons as an example, the official metro style has a number of predefined buttons :

image.png

 

To replicate the above, and do it in such a way that :

  • the component code (TW3Button) does not need any change
  • the standard theming classes still apply (TW3Button TW3ButtonBorder TW3ButtonBackground), but with different content of course
  • while allowing for these different predefined component versions

is to do something like this

procedure TForm1.InitializeForm;
begin
  inherited;
  // this is a good place to initialize components

  var Button1 : TW3Button := TW3Button.Create(self);
  Button1.SetBounds(20,20,83,40);
  Button1.Caption := 'Default';

  var Button2 : TW3Button := TW3Button.Create(self);
  Button2.SetBounds(110,20,83,40);
  Button2.Caption := 'Primary';
  Button2.TagStyle.Add('primary');

  var Button3 : TW3Button := TW3Button.Create(self);
  Button3.SetBounds(200,20,83,40);
  Button3.Caption := 'Secondary';
  Button3.TagStyle.Add('secondary');

  var Button4 : TW3Button := TW3Button.Create(self);
  Button4.SetBounds(290,20,83,40);
  Button4.Caption := 'Success';
  Button4.TagStyle.Add('success');
end;

 

result :

image.png

 

with the metro css file for the above looking like this

/*
//////////////////////////////////////////
// TW3Button (default)
//////////////////////////////////////////
*/

.TW3Button {
    font-weight: 400;
    text-align: center;
    white-space: nowrap;
    vertical-align: middle;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    border: 1px solid transparent;
    padding: 0 0.75rem;
    -webkit-transition: all 0.15s ease-in-out;
    transition: all 0.15s ease-in-out;
    background-color: #ebebeb;
    color: #1d1d1d;
    cursor: pointer;
    outline: none;
    -ms-touch-action: manipulation;
    touch-action: manipulation;
    -webkit-appearance: button;
    /*
    font-size: 0.875rem;
    line-height: 34px;
    height: 36px;
    */
}

.TW3Button:focus, .TW3Button:active {
    text-decoration: none;
    -webkit-box-shadow: 0 0 0 3px rgba(228, 228, 228, 0.45);
    box-shadow: 0 0 0 3px rgba(228, 228, 228, 0.45);
}

.TW3Button:hover {
    background-color: rgba(29, 29, 29, 0.1);
}

.TW3ButtonBackground {
}

.TW3ButtonBorder {
}

/*
//////////////////////////////////////////
// TW3Button Primary
//////////////////////////////////////////
*/

.TW3Button.primary {
    outline-color: #75b5fd;
    background-color: #0366d6;
    color: #ffffff;
}

.TW3Button.primary:hover {
    color: #ffffff;
    background-color: #024ea4;
    border-color: #023671;
}

.TW3Button.primary:focus, .TW3Button.primary:active {
    -webkit-box-shadow: 0 0 0 3px rgba(3, 102, 214, 0.45);
    box-shadow: 0 0 0 3px rgba(3, 102, 214, 0.45);
}

/*
//////////////////////////////////////////
// TW3Button Secondary
//////////////////////////////////////////
*/

.TW3Button.secondary {
    outline-color: #b7c6cd;
    background-color: #607d8b;
    color: #ffffff;
}

.TW3Button.secondary:hover {
    color: #ffffff;
    background-color: #4b626d;
    border-color: #36474f;
}

.TW3Button.secondary:focus, .TW3Button.secondary:active {
    -webkit-box-shadow: 0 0 0 3px rgba(96, 125, 139, 0.45);
    box-shadow: 0 0 0 3px rgba(96, 125, 139, 0.45);
}

/*
//////////////////////////////////////////
// TW3Button Success
//////////////////////////////////////////
*/

.TW3Button.success {
    outline-color: #adeb6e;
    background-color: #60a917;
    color: #ffffff;
}

.TW3Button.success:hover {
    color: #ffffff;
    background-color: #477c11;
    border-color: #2d4f0b;
}

.TW3Button.success:focus, .TW3Button.success:active {
    -webkit-box-shadow: 0 0 0 3px rgba(96, 169, 23, 0.45);
    box-shadow: 0 0 0 3px rgba(96, 169, 23, 0.45);
}

see demo (check click and hover states)

What do you think ?

 

By the way, this css file is made up by screenscraping, which is actually not too bad / timeconsuming. 
Alternatively it could also be composed out of relevant Tailwind functions, results would be very similar.

 

 

Link to post
Share on other sites
  • Moderators

Thanks

There are however a couple of minor / major (?) caveats doing it like this :

  • I made up some more metro components, and it becomes apparent that it is unavoidable to use more modifier classes: TagStyle.Add('primary image-button outline icon-left rounded etc).
  • this type of styling is not supported by the visual designer in the ide, so styling can be done in code only. Even though only one-liners (TagStayle.Add( )
  • (most rtl components (but not all) have a component, a border and a background theme class baked in. Not all of these can be reused, so sometimes the border and/or background classes may end up being empty)

probably the second bullet (visual designer) is the most important one for future enhancements

 

Link to post
Share on other sites
  • 8 months later...
  • Moderators

One of the plusses of the (opensource) tailwind css library is that it does not have any pre-made components like buttons etc.

Instead, any element can be styled using one or more of it's css 'functions'. So this also works for any of the standard SMS components.

As an example, see here the new login demo (Featured Demos / Forms & Components / LoginDialog), but styled a bit differently

Capture.jpg

Demo (see hover and focus effects)

Usage is quite intuitive :

-  Give the LoginForm a white background and rounded corners
-  Display the 'Please login' label in dark grey using a small bold font
-  Render the user name editbox with a shadow, rounded corners, dark grey text, a 3px lead and a shadow outline on hover and on focus
    etc

in code, just 5 one-liners :

procedure TLoginForm.InitializeForm;
begin
...

  self.handle.className := "bg-white rounded";     //dialog
  W3Label1.handle.className := "block text-gray-700 text-sm font-bold";     //title
  EditUserName.handle.className := "shadow appearance-none border rounded w-full px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline hover:outline-none hover:shadow-outline";
  EditPassWord.handle.className := "shadow appearance-none border border-red-500 rounded w-full px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline";
  LogIn.handle.className := "bg-blue-500 hover:bg-blue-700 text-white font-bold rounded focus:outline-none focus:shadow-outline";   //button

end;

 

( instead of manipulating the elements className directly by it's handle, use the rtl's built in TagStyle functions :

  self.TagStyle.Clear;
  self.TagStyle.Add("bg-white rounded");


 

 

Link to post
Share on other sites
  • Moderators

Various ways to do this. Probably easiest is to download the latest tailwind css file from

https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css

and store it in the lib folder and use the $R directive

initialization
  Forms.RegisterForm({$I %FILE%}, TForm1);
  {$R 'tailwind.css'}

This will automatically add a line like this to your index template file :

<link rel="stylesheet" href="lib/tailwind.css">

Alternatively, either add a custom template in the Project Manager and add the css file link manually to it, or load the css file in code

Apart from any $R directive, projectsource is the same as the official demo with the 5 one-liners added

Link to post
Share on other sites
  • Administrators
12 hours ago, IElite said:

@lynkfs I really like the idea of leveraging a framework like this and would love to see SMS support frameworks like this in the IDE. But, that may be wishful thinking <smile>

Well, I've been thinking about how to support this as it'd be great to be able to use standard stylesheets from other frameworks. That way we could have access to a lot of ready made stylesheets. The one I had a look at earlier was Bulma.

At the moment we do have background- and borderstyles like for example bsContainerBackground or btFlatBorder. What I'd like to test is to add a number of new styles and then create a mapping layer between them and the other frameworks. For example:

  • Add a background like bsErrorBackground
  • When a Bulma stylesheet is used, and bsErrorBackground is set, the mapping layer would translate that to Bulma's style: "is-problem"

In the same way, bsContainerBackground could be translated to Bulma's "box" style etc.

Link to post
Share on other sites

I've also tried tailwind and it's quite nice and relatively easy. Hardest part was understanding how to make it's css classes compatible with SMS, for example setting alignment usually do not work since tailwind style is in colision with absolute position (or left, right, top, bottom, widht and height).

Anyway for integration into SMS it would need an option in a control to specify what to emit when generating code, for example exclude absolute or left, right values.

I'm thinking of some kind of property on control like TCtrlOptions = (coExcludeAbsPos, coExcLeft, coExlWidth, coExcHeight)... and we could simply set up those in ObjectInspector. Probably to make these setting inherited from parent (like ParentFont), set soExclAbsPos on a panel with 20 children and all of them when created wouldn't include position: absolute.

Link to post
Share on other sites
  • Moderators

Smart has its own layout system and Tailwind has some layout functions as well.

So you're right, if you want to use them both, then you have to signal Smart what you want to do.

It also makes life somewhat complicated. Easiest is just to forget about the layout functions in any css framework (positioning, sizing), and just stick with the Smart layout system. That still leaves many css functions to do with colors, backgrounds, typography, borders, svg and effects

@jarto is looking at Bulma, which is sort of similar to Tailwind. Determining which of it's css functions would naturally fit into the ide is then the next question.

Link to post
Share on other sites

> It also makes life somewhat complicated. Easiest is just to forget about the layout functions in any css framework (positioning, sizing), and just

> stick with the Smart layout system. That still leaves many css functions to do with colors, backgrounds, typography, borders, svg and effects

It's probably matter of preference, I have very bad experience with SMS layout system in 2.2 version and to me it's preatty easy to get what I want with css. Haven't tried new one in 3.x version though.

I'm not sure it can handle adjusting to different screen sizes or adding spacing between controls or width/height settings (2/3 of parent, 1/4+ 2/5 and similar stuff which are quite easy with css).

Link to post
Share on other sites
  • Administrators
22 hours ago, IgorSavkic said:

It's probably matter of preference, I have very bad experience with SMS layout system in 2.2 version and to me it's preatty easy to get what I want with css. Haven't tried new one in 3.x version though.

I'm not sure it can handle adjusting to different screen sizes or adding spacing between controls or width/height settings (2/3 of parent, 1/4+ 2/5 and similar stuff which are quite easy with css).

The difference between 2.2 and 3 (and the latest alpha) in this area is absolutely astronomical.

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...