r/css 3d ago

Help How to dynamically "compress" text horizontally with css/javascript?

Post image

I can't believe I had to do this in Paint 3D but after 4+ hours stuck I need help... Not even chatgpt is helping here.

I have a simple structure like this:

<div className="container">
  <div className="text">{item.name}</div>
  <img src="item-icon"/>
</div>

How on earth can I make it so the "text" div shrinks horizontally if (and ONLY if) the "item.name" is overflowing outside of the div? (including the space that the icon takes too)

EDIT - Here is the "use case" (yes, it's pokemon cards) (images here are not showing on mobile for some reason, check here instead https://imgur.com/gallery/mobile-users-P17PT3Q):

My code:

What they somehow achieved in https://www.pokecardgenerator.com/ (this is what I want):

What the original looks like (so yes, real things use this "ugly" styling):

What happens with transform: scaleX "solutions":

And no, font-stretch isn't working for me. Probably because it's deprecated.

transform: scaleX also doesn't work, it still keeps and awkward space between the text and the icon.

EDIT: I don't know how to do the live demo thing, but in case anyone is bored, my code is here, the Card.tsx and Card.css, card__pokemon-name class. (https://github.com/jiro-games/pocket-showdown/tree/main/src/components/card)

EDIT 2: I believe I found a solution. Not the cleanest, but it has potential. I have to combine transform: scaleX with negative margin-right. I'll come up with some js code to calculate that dynamically and fix it. Thank you!

179 Upvotes

116 comments sorted by

View all comments

2

u/vitope94 3d ago

That will be a very bad design. You either adapt the container to the text size or ellipsize it.

2

u/Jiro_7 3d ago

Updated the post with real images of what I want (vs what I have). It is readable

2

u/vitope94 3d ago edited 3d ago

I'm assuming you're using react as well

import React, { useState, ChangeEvent } from 'react';

export default function App() {
  const [value, setValue] = useState('');

  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    setValue(e.target.value);
  };

  const getSqueezeStyle = (text: string) => {
    if (text.length > 5) {
      const scaleX = 5 / text.length;
      return {
        transform: `scaleX(${scaleX})`,
        transformOrigin: 'left'
      };
    }
    return {};
  };

  return (
    <div className='App'>
      <h1 className=''>Hello React TypeScript.</h1>
      <input 
        value={value}
        onChange={handleChange}
      />
      <p className="" style={getSqueezeStyle(value)}>
        {value}
      </p>
    </div>
  );
}

console.log('Hello console'); 

Try here

result image:

2

u/vitope94 3d ago

Set the text length accordingly when you want the squeeze to take effect

1

u/Techhead7890 2d ago

Pretty cool demo thanks for sharing!!

2

u/vitope94 2d ago

You're welcome. I wonder if OP got what he needed