Add a style class to each text-element in textfield

Is it possible to add a class: class-name either at the beginning or at the end of a paragraph, or any other markdown element?

This is text (class: font--serif)

> look at this (class: color--green)
```

This would then be used to set stylistic attributes to each portion of the text, e.g. font-family, color, etc.

I found this plugin by @fabianmichael, but, as far as I can see, you can set one class per each element.

The closest thing would be to use a kirby-tag, something like:

```
<?php

kirbytext::$tags['font'] = array(
  'html' => function($tag) {
    return 'class="$tag->attr('font')"';
  }
);
```

the problem is that I don't know how to attach it to each tag that markdown outputs.

Would this be possible?

Thank you!

Yes, the Classy Markdown plugin always adds the same class to each type of element, so it seems that’s not what you want.

If you enable Markdown extra, you can attach classes to headers, images, links and fenced code blocks, but not to paragraphs or any other tags.

With Markdown extra enabled, you can use html tags within Markdown while preserving the ability to add markdown formatted text inside:

<div class="some_class" markdown=1> Some **markdown** formatted text here</div>

Other than that, you can of course use Kirbytags, but then you would have to include all text you want to add a class to within those tags.

I don’t know what exactly you want to achieve, maybe there are other options like using the Kirby builder plugin and create a new builder item for each element?

Thank you!

I think that the closest thing would be to use the Kirby builder plugin, even though it is gonna break the workflow from writing a text on a local app and doing only one copy-paste to the text-field in the kirby panel.

Using markdown-extra with div's seems a bit too much, to keep the writing flow pleasant.

Adding hundreds of classes to a text does disrupt the workflow no matter how you solve that, I guess.

Yes true, but still there is a difference between having to wrap inside div's your text and just having to write (class: class-name) at the end of your markdown element, or as you suggested, using the builder plugin (which seems the best thing to experiment with what I have to do).

:smiley:

An alternative would be to put the class in front of the text to style and then use some regex voodoo to extract the class and add it to the next tag using a Kirbytext post filter.

That was my first idea, but I know nothing of regex!

Something like a preg_match function?

preg_match("'<p class=\"review\">(.*?)</p>'si", $source, $match);
if($match) echo "result=".$match[1];

Yes, preg_match() is the way to go.

You would look for a pattern like this \(\(class:(.*)\))(<.*>)\, which would get you

(class: some_class)<p>

or

(class: some_class)<h1>

and then you would replace those matching groups.

Edit: http://regexr.com

Edit; have a look at the columns plugin

Edit 3: You should probably make that more fool proof by allowing one or more spaces between the colon and the class… you never know) and maybe between the class and the tag, you need to test this.

1 Like

So, this is what I reached so far, I parsed class-name from (class: class-name), but I don’t know how to add it inside a class=" " in the opening html tag that markdown produces: eg,

<h1 class="class-name">text</h1>
<?php

  kirbytext::$post[] = function($kirbytext, $text) {

    // 1. find and match string containing `(class: class-name)`

    $text = preg_replace_callback('!\(class:\s(.*)\)\s(<.*>)!', function($matches) use($kirbytext) {

      // 2. copy text in string after `: ` and save it in a variable
      $class_name = preg_split('!\:\s(.*?)\)!', $matches[1]);

      // 3. add new string variable to next html tag
      $html_tag = preg_split //one more preg_split?

    }, $text);

    return $text;
};

How would you proceed?

Hm, it’s not as easy as I thought and maybe too error prone after all. In fact, when used as a post filter, the html is already rendered, but as a pre filter, it’s impossible to fetch the tags. Maybe it’s safer to do it with a pre filter like the columns tag:

(class: some_class)
bla bla bla
(endclass)

Or back to the builder idea.

I see, probably that’s why the columns.php plugin works with that syntax. And in a way, that syntax is more explicit also for the user to interact with.

The only thing is, if I tweak the columns.php, then it will wrap the text I want to style in a div, right?

Do you think there is any way I can avoid that?

My only idea while working on this yesterday was to add to the regex function a list of all markdown output elements, hoping I would have been able in this way to prevent the regex to select alsol the closing html tag.

I’m gonna give it a try and will report back!

This is the updated code so far, with the syntax in the panel being:

(style: font--sans)
The current discourse around the Stack remains within the traditional geopolitical frames responsible for its emergence: in its birth, the Stack is already colonized. 
(endstyle)
// 1. match text from (style: class-name) to (endstyle)
  $text = preg_replace_callback('!\(style:|\s(.*)\)(.*)\(endstyle\)!is', function($matches) use($kirbytext) {

    // 2. select text in string after `: ` and save it in a variable
    $style_classes = preg_split('!\:\s(.*?)\)!', $matches[1]);

    // 3. select text between parentheses and save it in a variable
    $styled_texts = preg_split('!\(style(:)|\s(.*)\)(.*)\(endstyle\)!', $matches[2]);
    $html         = array();

    foreach($styled_texts as $styled_text) {
      $field  = new Field($kirbytext->field->page, null, trim($styled_text));
      $html[] = kirbytext($field);
    }

    return '<div class="' . $style_classes . '">' . 'implode($html)' . '</div>';

  }, $text);

  return $text;

It throws the following error

Method Kirbytext::__toString() must return a string value

and this, referring to
website/kirby/helpers/php

Probably the error is caused by this line at the end?

return '<div class="' . $style_classes . '">' . 'implode($html)' . '</div>';
kirbytext::$pre[] = function($kirbytext, $text) {

  // 1. match text from (style: class-name) to (endstyle)
  $text = preg_replace_callback('!\(style:|\s(.*)\)(.*)\(endstyle\)!is', function($matches) use($kirbytext) {

    // 2. select text in string after `: ` and save it in a variable
    $style_classes = preg_split('!\:\s(.*?)\)!', $matches[1]);

    // 3. select text between parentheses and save it in a variable
    $styled_texts = preg_split('!\(style(:)|\s(.*)\)(.*)\(endstyle\)!', $matches[2]);
    $html         = array();

    foreach($styled_texts as $styled_text) {
      $field  = new Field($kirbytext->field->page, null, trim($styled_text));
      $html[] = kirbytext($field);
    }

    return '<div class="' . $style_classes . '">' . 'implode($html)' . '</div>';

  }, $text);

  return $text;

For future reference, here the kirby pre-hook plugin that:
— wraps a portion of text around an html tag
— adds a class with multiple values to the html tag

The syntax to use in the text field is as follows:

(style: class-name class-name)

Text *text* etc.

(style)

This the code:

<?php

/**
 * Text-style Plugin
 *
 */

kirbytext::$pre[] = function($kirbytext, $text) {

  // 1. match text from (style: class-name) to (style)
  $text = preg_replace_callback('!\(style:(.*?)\)(.*?)\(style\)!is', function($matches) use($kirbytext) {

  // 2. select text in string after `: ` and save it in a variable
  $style_class = preg_split('!\:\s(.*?)\)!', trim($matches[1]));

  // 3. select text between parentheses and save it in a variable
  $styled_texts = preg_split('!\(style:(.*?)\)(.*?)\(style\)!', $matches[2]);
  $html         = array();

  // 4. parse text as kirbytext
  foreach($styled_texts as $styled_text) {
    $field  = new Field($kirbytext->field->page, null, trim($styled_text));
    $html[] = kirbytext($field);
  }

  // 5. return text
  return '<div class="' . implode($style_class) . '">' . implode($html) . '</div>';

  }, $text);

  return $text;

};

A rebound : A plugin to create Sections with class(es) in Kirby 3:

In use:

(section: dark)
The text
(section)

The index.php file:

<?php
Kirby::plugin('kirby/sections', [
  'hooks' => [
    'kirbytags:before' => function ($text, array $data = []) {
      
    // 1. match text from (style: class-name) to (style)
    $text = preg_replace_callback('!\(section:(.*?)\)(.*?)\(section\)!is', function($matches) use($text, $data) {

    // 2. select text in string after `: ` and save it in a variable
    $style_class = preg_split('!\:\s(.*?)\)!', trim($matches[1]));

    // 3. select text between parentheses and save it in a variable
    $styled_texts = preg_split('!\(section:(.*?)\)(.*?)\(section\)!', $matches[2]);
    $html         = array();

    // 4. parse text as kirbytext
    foreach($styled_texts as $styled_text) {
      //$field  = new Field($kirbytext->field->page, null, trim($styled_text));
      $field  = trim($styled_text);
      $html[] = kirbytext($field);
    }

    // 5. return text
    return '<section class="section ' . implode($style_class) . '">' . implode($html) . '</section>';

    }, $text);

    return $text;
    }
  ]
]); 

`

1 Like